mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-01 10:45:01 +08:00
Merge remote-tracking branch 'origin/GP-6097_ghidragon_read_write_lock--SQUASHED'
This commit is contained in:
+23
-23
@@ -286,7 +286,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceAddressPropertyManager createAddressPropertyManager(
|
protected DBTraceAddressPropertyManager createAddressPropertyManager(
|
||||||
DBTraceThreadManager threadManager) throws CancelledException, IOException {
|
DBTraceThreadManager threadManager) throws CancelledException, IOException {
|
||||||
return createTraceManager("Address Property Manager",
|
return createTraceManager("Address Property Manager",
|
||||||
(openMode, monitor) -> new DBTraceAddressPropertyManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceAddressPropertyManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager));
|
baseLanguage, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceBookmarkManager createBookmarkManager(DBTraceThreadManager threadManager)
|
protected DBTraceBookmarkManager createBookmarkManager(DBTraceThreadManager threadManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Bookmark Manager",
|
return createTraceManager("Bookmark Manager",
|
||||||
(openMode, monitor) -> new DBTraceBookmarkManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceBookmarkManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager));
|
baseLanguage, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceBreakpointManager createBreakpointManager(DBTraceObjectManager objectManager)
|
protected DBTraceBreakpointManager createBreakpointManager(DBTraceObjectManager objectManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Breakpoint Manager",
|
return createTraceManager("Breakpoint Manager",
|
||||||
(openMode, monitor) -> new DBTraceBreakpointManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceBreakpointManager(dbh, openMode, lock, monitor,
|
||||||
this, objectManager));
|
this, objectManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager)
|
DBTraceOverlaySpaceAdapter overlayAdapter, DBTraceReferenceManager referenceManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Code Manager",
|
return createTraceManager("Code Manager",
|
||||||
(openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceCodeManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager, platformManager, dataTypeManager, overlayAdapter,
|
baseLanguage, this, threadManager, platformManager, dataTypeManager, overlayAdapter,
|
||||||
referenceManager));
|
referenceManager));
|
||||||
}
|
}
|
||||||
@@ -321,7 +321,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceCommentAdapter createCommentAdapter(DBTraceThreadManager threadManager)
|
protected DBTraceCommentAdapter createCommentAdapter(DBTraceThreadManager threadManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Comment Adapter",
|
return createTraceManager("Comment Adapter",
|
||||||
(openMode, monitor) -> new DBTraceCommentAdapter(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceCommentAdapter(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager));
|
baseLanguage, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceDataSettingsAdapter createDataSettingsAdapter(
|
protected DBTraceDataSettingsAdapter createDataSettingsAdapter(
|
||||||
DBTraceThreadManager threadManager) throws CancelledException, IOException {
|
DBTraceThreadManager threadManager) throws CancelledException, IOException {
|
||||||
return createTraceManager("Data Settings Adapter",
|
return createTraceManager("Data Settings Adapter",
|
||||||
(openMode, monitor) -> new DBTraceDataSettingsAdapter(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceDataSettingsAdapter(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager));
|
baseLanguage, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceDataTypeManager createDataTypeManager(DBTracePlatformManager platformManager)
|
protected DBTraceDataTypeManager createDataTypeManager(DBTracePlatformManager platformManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Data Type Manager", (openMode,
|
return createTraceManager("Data Type Manager", (openMode,
|
||||||
monitor) -> new DBTraceDataTypeManager(dbh, openMode, rwLock, monitor, this,
|
monitor) -> new DBTraceDataTypeManager(dbh, openMode, lock, monitor, this,
|
||||||
platformManager.getHostPlatform()));
|
platformManager.getHostPlatform()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceEquateManager createEquateManager(DBTraceThreadManager threadManager)
|
protected DBTraceEquateManager createEquateManager(DBTraceThreadManager threadManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Equate Manager",
|
return createTraceManager("Equate Manager",
|
||||||
(openMode, monitor) -> new DBTraceEquateManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceEquateManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager));
|
baseLanguage, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTracePlatformManager createPlatformManager()
|
protected DBTracePlatformManager createPlatformManager()
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Platform Manager",
|
return createTraceManager("Platform Manager",
|
||||||
(openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTracePlatformManager(dbh, openMode, lock, monitor,
|
||||||
baseCompilerSpec, this));
|
baseCompilerSpec, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceMemoryManager createMemoryManager(DBTraceThreadManager threadManager,
|
protected DBTraceMemoryManager createMemoryManager(DBTraceThreadManager threadManager,
|
||||||
DBTraceOverlaySpaceAdapter overlayAdapter) throws IOException, CancelledException {
|
DBTraceOverlaySpaceAdapter overlayAdapter) throws IOException, CancelledException {
|
||||||
return createTraceManager("Memory Manager",
|
return createTraceManager("Memory Manager",
|
||||||
(openMode, monitor) -> new DBTraceMemoryManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceMemoryManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager, overlayAdapter));
|
baseLanguage, this, threadManager, overlayAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,14 +369,14 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceModuleManager createModuleManager(DBTraceObjectManager objectManager)
|
protected DBTraceModuleManager createModuleManager(DBTraceObjectManager objectManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Module Manager",
|
return createTraceManager("Module Manager",
|
||||||
(openMode, monitor) -> new DBTraceModuleManager(dbh, openMode, rwLock, monitor, this,
|
(openMode, monitor) -> new DBTraceModuleManager(dbh, openMode, lock, monitor, this,
|
||||||
objectManager));
|
objectManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DependentService
|
@DependentService
|
||||||
protected DBTraceObjectManager createObjectManager() throws CancelledException, IOException {
|
protected DBTraceObjectManager createObjectManager() throws CancelledException, IOException {
|
||||||
return createTraceManager("Object Manager",
|
return createTraceManager("Object Manager",
|
||||||
(openMode, monitor) -> new DBTraceObjectManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceObjectManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this));
|
baseLanguage, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,14 +384,14 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceOverlaySpaceAdapter createOverlaySpaceAdapter()
|
protected DBTraceOverlaySpaceAdapter createOverlaySpaceAdapter()
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Overlay Space Adapter", (openMode,
|
return createTraceManager("Overlay Space Adapter", (openMode,
|
||||||
monitor) -> new DBTraceOverlaySpaceAdapter(dbh, openMode, rwLock, monitor, this));
|
monitor) -> new DBTraceOverlaySpaceAdapter(dbh, openMode, lock, monitor, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DependentService
|
@DependentService
|
||||||
protected DBTraceReferenceManager createReferenceManager(DBTraceThreadManager threadManager,
|
protected DBTraceReferenceManager createReferenceManager(DBTraceThreadManager threadManager,
|
||||||
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
||||||
return createTraceManager("Reference Manager",
|
return createTraceManager("Reference Manager",
|
||||||
(openMode, monitor) -> new DBTraceReferenceManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceReferenceManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager, overlayAdapter));
|
baseLanguage, this, threadManager, overlayAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
DBTraceThreadManager threadManager, DBTracePlatformManager platformManager)
|
DBTraceThreadManager threadManager, DBTracePlatformManager platformManager)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Context Manager",
|
return createTraceManager("Context Manager",
|
||||||
(openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceRegisterContextManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager, platformManager));
|
baseLanguage, this, threadManager, platformManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceStackManager createStackManager(DBTraceThreadManager threadManager,
|
protected DBTraceStackManager createStackManager(DBTraceThreadManager threadManager,
|
||||||
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
||||||
return createTraceManager("Stack Manager",
|
return createTraceManager("Stack Manager",
|
||||||
(openMode, monitor) -> new DBTraceStackManager(dbh, openMode, rwLock, monitor, this,
|
(openMode, monitor) -> new DBTraceStackManager(dbh, openMode, lock, monitor, this,
|
||||||
threadManager, overlayAdapter));
|
threadManager, overlayAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceStaticMappingManager createStaticMappingManager(
|
protected DBTraceStaticMappingManager createStaticMappingManager(
|
||||||
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
|
||||||
return createTraceManager("Static Mapping Manager",
|
return createTraceManager("Static Mapping Manager",
|
||||||
(openMode, monitor) -> new DBTraceStaticMappingManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceStaticMappingManager(dbh, openMode, lock, monitor,
|
||||||
this, overlayAdapter));
|
this, overlayAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter)
|
DBTraceDataTypeManager dataTypeManager, DBTraceOverlaySpaceAdapter overlayAdapter)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
return createTraceManager("Symbol Manager",
|
return createTraceManager("Symbol Manager",
|
||||||
(openMode, monitor) -> new DBTraceSymbolManager(dbh, openMode, rwLock, monitor,
|
(openMode, monitor) -> new DBTraceSymbolManager(dbh, openMode, lock, monitor,
|
||||||
baseLanguage, this, threadManager, dataTypeManager, overlayAdapter));
|
baseLanguage, this, threadManager, dataTypeManager, overlayAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +433,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceThreadManager createThreadManager(DBTraceObjectManager objectManager)
|
protected DBTraceThreadManager createThreadManager(DBTraceObjectManager objectManager)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
return createTraceManager("Thread Manager",
|
return createTraceManager("Thread Manager",
|
||||||
(openMode, monitor) -> new DBTraceThreadManager(dbh, openMode, rwLock, monitor, this,
|
(openMode, monitor) -> new DBTraceThreadManager(dbh, openMode, lock, monitor, this,
|
||||||
objectManager));
|
objectManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +441,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
protected DBTraceTimeManager createTimeManager(DBTraceThreadManager threadManager)
|
protected DBTraceTimeManager createTimeManager(DBTraceThreadManager threadManager)
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
return createTraceManager("Time Manager", (openMode, monitor) -> new DBTraceTimeManager(dbh,
|
return createTraceManager("Time Manager", (openMode, monitor) -> new DBTraceTimeManager(dbh,
|
||||||
openMode, rwLock, monitor, this, threadManager));
|
openMode, lock, monitor, this, threadManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -650,12 +650,12 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LockHold lockRead() {
|
public LockHold lockRead() {
|
||||||
return LockHold.lock(rwLock.readLock());
|
return LockHold.lock(lock.readLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LockHold lockWrite() {
|
public LockHold lockWrite() {
|
||||||
return LockHold.lock(rwLock.writeLock());
|
return LockHold.lock(lock.writeLock());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sourceArchiveChanged(UniversalID sourceArchiveID) {
|
public void sourceArchiveChanged(UniversalID sourceArchiveID) {
|
||||||
@@ -755,7 +755,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearCache(boolean all) {
|
protected void clearCache(boolean all) {
|
||||||
try (LockHold hold = LockHold.lock(rwLock.writeLock())) {
|
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||||
for (DBTraceManager m : managers) {
|
for (DBTraceManager m : managers) {
|
||||||
m.invalidateCache(all);
|
m.invalidateCache(all);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-14
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -78,7 +78,7 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
|
|
||||||
protected abstract V doGetContaining(K key);
|
protected abstract V doGetContaining(K key);
|
||||||
|
|
||||||
protected List<? extends T> getAllInRangeCacheContaining(K key) {
|
protected synchronized List<? extends T> getAllInRangeCacheContaining(K key) {
|
||||||
List<T> result = new ArrayList<>();
|
List<T> result = new ArrayList<>();
|
||||||
for (Entry<TraceAddressSnapRange, T> ent : rangeCache) {
|
for (Entry<TraceAddressSnapRange, T> ent : rangeCache) {
|
||||||
TraceAddressSnapRange range = ent.getKey();
|
TraceAddressSnapRange range = ent.getKey();
|
||||||
@@ -93,7 +93,7 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T getFirstInRangeCacheContaining(K key) {
|
protected synchronized T getFirstInRangeCacheContaining(K key) {
|
||||||
for (Entry<TraceAddressSnapRange, T> ent : rangeCache) {
|
for (Entry<TraceAddressSnapRange, T> ent : rangeCache) {
|
||||||
TraceAddressSnapRange range = ent.getKey();
|
TraceAddressSnapRange range = ent.getKey();
|
||||||
if (!range.getLifespan().contains(key.snap)) {
|
if (!range.getLifespan().contains(key.snap)) {
|
||||||
@@ -107,12 +107,12 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isInCachedRange(long snap, Address address) {
|
protected synchronized boolean isInCachedRange(long snap, Address address) {
|
||||||
return rangeCacheRange != null && rangeCacheRange.getLifespan().contains(snap) &&
|
return rangeCacheRange != null && rangeCacheRange.getLifespan().contains(snap) &&
|
||||||
rangeCacheRange.getRange().contains(address);
|
rangeCacheRange.getRange().contains(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ensureInCachedRange(long snap, Address address) {
|
protected synchronized void ensureInCachedRange(long snap, Address address) {
|
||||||
if (isInCachedRange(snap, address)) {
|
if (isInCachedRange(snap, address)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -120,15 +120,15 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
loadRangeCache(rangeCacheRange = computeNewCachedRange(snap, address));
|
loadRangeCache(rangeCacheRange = computeNewCachedRange(snap, address));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TraceAddressSnapRange computeNewCachedRange(long snap, Address address) {
|
protected synchronized TraceAddressSnapRange computeNewCachedRange(long snap, Address address) {
|
||||||
return ImmutableTraceAddressSnapRange.centered(address, snap, addressBreadth, snapBreadth);
|
return ImmutableTraceAddressSnapRange.centered(address, snap, addressBreadth, snapBreadth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public V getContaining(K key) {
|
public synchronized V getContaining(K key) {
|
||||||
return pointCache.computeIfAbsent(key, this::doGetContaining);
|
return pointCache.computeIfAbsent(key, this::doGetContaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyNewEntry(Lifespan lifespan, Address address, T item) {
|
public synchronized void notifyNewEntry(Lifespan lifespan, Address address, T item) {
|
||||||
// TODO: Can this be smarter?
|
// TODO: Can this be smarter?
|
||||||
pointCache.clear();
|
pointCache.clear();
|
||||||
if (rangeCacheRange != null &&
|
if (rangeCacheRange != null &&
|
||||||
@@ -139,7 +139,7 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyNewEntry(Lifespan lifespan, AddressRange range, T item) {
|
public synchronized void notifyNewEntry(Lifespan lifespan, AddressRange range, T item) {
|
||||||
// TODO: Can this be smarter?
|
// TODO: Can this be smarter?
|
||||||
pointCache.clear();
|
pointCache.clear();
|
||||||
if (rangeCacheRange != null &&
|
if (rangeCacheRange != null &&
|
||||||
@@ -150,7 +150,7 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyNewEntries(Lifespan lifespan, AddressSetView addresses, T item) {
|
public synchronized void notifyNewEntries(Lifespan lifespan, AddressSetView addresses, T item) {
|
||||||
// TODO: Can this be smarter?
|
// TODO: Can this be smarter?
|
||||||
pointCache.clear();
|
pointCache.clear();
|
||||||
if (rangeCacheRange != null &&
|
if (rangeCacheRange != null &&
|
||||||
@@ -164,17 +164,18 @@ public abstract class DBTraceCacheForContainingQueries<K extends GetKey, V, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyEntryRemoved(Lifespan lifespan, AddressRange range, T item) {
|
public synchronized void notifyEntryRemoved(Lifespan lifespan, AddressRange range, T item) {
|
||||||
// TODO: Can this be smarter?
|
// TODO: Can this be smarter?
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyEntryShapeChanged(Lifespan lifespan, AddressRange range, T item) {
|
public synchronized void notifyEntryShapeChanged(Lifespan lifespan, AddressRange range,
|
||||||
|
T item) {
|
||||||
// TODO: Can this be smarter?
|
// TODO: Can this be smarter?
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public synchronized void invalidate() {
|
||||||
pointCache.clear();
|
pointCache.clear();
|
||||||
rangeCache.clear();
|
rangeCache.clear();
|
||||||
rangeCacheRange = null;
|
rangeCacheRange = null;
|
||||||
|
|||||||
+9
-8
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -41,7 +41,7 @@ public abstract class DBTraceCacheForSequenceQueries<T> {
|
|||||||
this.max = range.getMaxAddress();
|
this.max = range.getMaxAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getFloor(Address address) {
|
public synchronized T getFloor(Address address) {
|
||||||
Entry<Address, T> floor = nav.floorEntry(address);
|
Entry<Address, T> floor = nav.floorEntry(address);
|
||||||
if (floor != null) {
|
if (floor != null) {
|
||||||
return floor.getValue();
|
return floor.getValue();
|
||||||
@@ -60,7 +60,7 @@ public abstract class DBTraceCacheForSequenceQueries<T> {
|
|||||||
return ent.getValue();
|
return ent.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getCeiling(Address address) {
|
public synchronized T getCeiling(Address address) {
|
||||||
Entry<Address, T> ceiling = nav.ceilingEntry(address);
|
Entry<Address, T> ceiling = nav.ceilingEntry(address);
|
||||||
if (ceiling != null) {
|
if (ceiling != null) {
|
||||||
return ceiling.getValue();
|
return ceiling.getValue();
|
||||||
@@ -79,19 +79,20 @@ public abstract class DBTraceCacheForSequenceQueries<T> {
|
|||||||
return ent.getValue();
|
return ent.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load(
|
public synchronized void load(
|
||||||
ArrayList<? extends Entry<? extends TraceAddressSnapRange, ? extends T>> entries) {
|
ArrayList<? extends Entry<? extends TraceAddressSnapRange, ? extends T>> entries) {
|
||||||
for (Entry<? extends TraceAddressSnapRange, ? extends T> ent : entries) {
|
for (Entry<? extends TraceAddressSnapRange, ? extends T> ent : entries) {
|
||||||
nav.put(ent.getKey().getX1(), ent.getValue());
|
nav.put(ent.getKey().getX1(), ent.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean contains(Address address) {
|
protected synchronized boolean contains(Address address) {
|
||||||
return min.hasSameAddressSpace(address) && min.compareTo(address) <= 0 &&
|
return min.hasSameAddressSpace(address) && min.compareTo(address) <= 0 &&
|
||||||
max.compareTo(address) >= 0;
|
max.compareTo(address) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void reInit(@SuppressWarnings("hiding") long snap, AddressRange range) {
|
protected synchronized void reInit(@SuppressWarnings("hiding") long snap,
|
||||||
|
AddressRange range) {
|
||||||
this.snap = snap;
|
this.snap = snap;
|
||||||
this.min = range.getMinAddress();
|
this.min = range.getMinAddress();
|
||||||
this.max = range.getMaxAddress();
|
this.max = range.getMaxAddress();
|
||||||
@@ -101,7 +102,7 @@ public abstract class DBTraceCacheForSequenceQueries<T> {
|
|||||||
protected final int maxRegions;
|
protected final int maxRegions;
|
||||||
protected final int addressBreadth;
|
protected final int addressBreadth;
|
||||||
// TODO: Depending on the number of regions, LinkedList may perform better
|
// TODO: Depending on the number of regions, LinkedList may perform better
|
||||||
protected final List<CachedRegion> cache = new ArrayList<>();
|
protected final List<CachedRegion> cache = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
public DBTraceCacheForSequenceQueries(int maxRegions, int addressBreadth) {
|
public DBTraceCacheForSequenceQueries(int maxRegions, int addressBreadth) {
|
||||||
this.maxRegions = maxRegions;
|
this.maxRegions = maxRegions;
|
||||||
|
|||||||
+7
-5
@@ -65,13 +65,13 @@ public abstract class AbstractBaseDBTraceDefinedUnitsView<T extends AbstractDBTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadRangeCache(TraceAddressSnapRange range) {
|
protected synchronized void loadRangeCache(TraceAddressSnapRange range) {
|
||||||
rangeCache.addAll(
|
rangeCache.addAll(
|
||||||
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range)).entries());
|
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(range)).entries());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected T doGetContaining(GetKey key) {
|
protected synchronized T doGetContaining(GetKey key) {
|
||||||
ensureInCachedRange(key.snap, key.addr);
|
ensureInCachedRange(key.snap, key.addr);
|
||||||
return getFirstInRangeCacheContaining(key);
|
return getFirstInRangeCacheContaining(key);
|
||||||
}
|
}
|
||||||
@@ -87,14 +87,15 @@ public abstract class AbstractBaseDBTraceDefinedUnitsView<T extends AbstractDBTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadCachedRegion(CachedRegion region) {
|
protected synchronized void loadCachedRegion(CachedRegion region) {
|
||||||
region.load(new ArrayList<>(
|
region.load(new ArrayList<>(
|
||||||
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(region.min, region.max,
|
mapSpace.reduce(TraceAddressSnapRangeQuery.intersecting(region.min, region.max,
|
||||||
region.snap, region.snap)).entries()));
|
region.snap, region.snap)).entries()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Entry<TraceAddressSnapRange, T> doFloorEntry(long snap, Address max) {
|
protected synchronized Entry<TraceAddressSnapRange, T> doFloorEntry(long snap,
|
||||||
|
Address max) {
|
||||||
Address spaceMin = space.space.getMinAddress();
|
Address spaceMin = space.space.getMinAddress();
|
||||||
return mapSpace
|
return mapSpace
|
||||||
.reduce(TraceAddressSnapRangeQuery.intersecting(spaceMin, max, snap, snap)
|
.reduce(TraceAddressSnapRangeQuery.intersecting(spaceMin, max, snap, snap)
|
||||||
@@ -103,7 +104,8 @@ public abstract class AbstractBaseDBTraceDefinedUnitsView<T extends AbstractDBTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Entry<TraceAddressSnapRange, T> doCeilingEntry(long snap, Address min) {
|
protected synchronized Entry<TraceAddressSnapRange, T> doCeilingEntry(long snap,
|
||||||
|
Address min) {
|
||||||
Address spaceMax = space.space.getMaxAddress();
|
Address spaceMax = space.space.getMaxAddress();
|
||||||
return mapSpace
|
return mapSpace
|
||||||
.reduce(TraceAddressSnapRangeQuery.intersecting(min, spaceMax, snap, snap))
|
.reduce(TraceAddressSnapRangeQuery.intersecting(min, spaceMax, snap, snap))
|
||||||
|
|||||||
+4
-4
@@ -366,7 +366,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
@Override
|
@Override
|
||||||
public Address getFallThrough() {
|
public Address getFallThrough() {
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
checkIsValid();
|
refreshIfNeeded();
|
||||||
if (isFallThroughOverridden()) {
|
if (isFallThroughOverridden()) {
|
||||||
DBTraceReferenceSpace refSpace = space.referenceManager.get(space.space, false);
|
DBTraceReferenceSpace refSpace = space.referenceManager.get(space.space, false);
|
||||||
if (refSpace == null) {
|
if (refSpace == null) {
|
||||||
@@ -392,7 +392,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
@Override
|
@Override
|
||||||
public Address getFallFrom() {
|
public Address getFallFrom() {
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
checkIsValid();
|
refreshIfNeeded();
|
||||||
// Go back one, considering alignment
|
// Go back one, considering alignment
|
||||||
DBTraceInstruction ins = this;
|
DBTraceInstruction ins = this;
|
||||||
int alignment = Math.min(1, getLanguage().getInstructionAlignment());
|
int alignment = Math.min(1, getLanguage().getInstructionAlignment());
|
||||||
@@ -515,7 +515,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
@Override
|
@Override
|
||||||
public boolean hasFallthrough() {
|
public boolean hasFallthrough() {
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
checkIsValid();
|
refreshIfNeeded();
|
||||||
if (isFallThroughOverridden()) {
|
if (isFallThroughOverridden()) {
|
||||||
return getFallThrough() != null; // dest stored as reference
|
return getFallThrough() != null; // dest stored as reference
|
||||||
}
|
}
|
||||||
@@ -646,7 +646,7 @@ public class DBTraceInstruction extends AbstractDBTraceCodeUnit<DBTraceInstructi
|
|||||||
return getBytes();
|
return getBytes();
|
||||||
}
|
}
|
||||||
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
|
||||||
checkIsValid();
|
refreshIfNeeded();
|
||||||
int len = getPrototype().getLength();
|
int len = getPrototype().getLength();
|
||||||
byte[] b = new byte[len];
|
byte[] b = new byte[len];
|
||||||
Address addr = getAddress();
|
Address addr = getAddress();
|
||||||
|
|||||||
+11
-15
@@ -79,19 +79,19 @@ public abstract class ByteCache {
|
|||||||
return new Page();
|
return new Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canCache(Address address, int len) {
|
public synchronized boolean canCache(Address address, int len) {
|
||||||
long cacheBufOff = address.getOffset() & ~OFFSET_MASK;
|
long cacheBufOff = address.getOffset() & ~OFFSET_MASK;
|
||||||
return cacheBufOff + len < pageCount * SIZE;
|
return cacheBufOff + len < pageCount * SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte read(Address address) throws MemoryAccessException {
|
public synchronized byte read(Address address) throws MemoryAccessException {
|
||||||
Address pageStart = address.getNewAddress(address.getOffset() & OFFSET_MASK);
|
Address pageStart = address.getNewAddress(address.getOffset() & OFFSET_MASK);
|
||||||
Page page = ensurePageCached(pageStart, 1);
|
Page page = ensurePageCached(pageStart, 1);
|
||||||
int cacheBufOff = (int) address.subtract(pageStart);
|
int cacheBufOff = (int) address.subtract(pageStart);
|
||||||
return page.bytes[cacheBufOff];
|
return page.bytes[cacheBufOff];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(Address address, ByteBuffer buf) throws MemoryAccessException {
|
public synchronized int read(Address address, ByteBuffer buf) throws MemoryAccessException {
|
||||||
long startOff = address.getOffset();
|
long startOff = address.getOffset();
|
||||||
long startPage = startOff & OFFSET_MASK;
|
long startPage = startOff & OFFSET_MASK;
|
||||||
int bufStart = buf.position();
|
int bufStart = buf.position();
|
||||||
@@ -108,7 +108,7 @@ public abstract class ByteCache {
|
|||||||
return buf.position() - bufStart;
|
return buf.position() - bufStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int choosePage(Address address, int len) {
|
private int choosePage(Address address, int len) {
|
||||||
for (int i = 0; i < pageCount; i++) {
|
for (int i = 0; i < pageCount; i++) {
|
||||||
Page page = pages[i];
|
Page page = pages[i];
|
||||||
if (page.contains(address, len)) {
|
if (page.contains(address, len)) {
|
||||||
@@ -135,21 +135,17 @@ public abstract class ByteCache {
|
|||||||
if (chosen == 0) {
|
if (chosen == 0) {
|
||||||
return pages[0];
|
return pages[0];
|
||||||
}
|
}
|
||||||
synchronized (pages) {
|
Page temp = pages[chosen];
|
||||||
Page temp = pages[chosen];
|
pages[chosen] = pages[0];
|
||||||
pages[chosen] = pages[0];
|
pages[0] = temp;
|
||||||
pages[0] = temp;
|
return temp;
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int doLoad(Address address, ByteBuffer buf) throws MemoryAccessException;
|
protected abstract int doLoad(Address address, ByteBuffer buf) throws MemoryAccessException;
|
||||||
|
|
||||||
public void invalidate(AddressRange range) {
|
public synchronized void invalidate(AddressRange range) {
|
||||||
synchronized (pages) {
|
for (Page p : pages) {
|
||||||
for (Page p : pages) {
|
p.invalidate(range);
|
||||||
p.invalidate(range);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-5
@@ -54,11 +54,6 @@ public class DBTraceProgramViewBookmarkManager implements TraceProgramViewBookma
|
|||||||
return bookmarkManager.defineBookmarkType(type, icon, color, priority);
|
return bookmarkManager.defineBookmarkType(type, icon, color, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDefinedType(String type) {
|
|
||||||
return bookmarkManager.isDefinedType(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BookmarkType[] getBookmarkTypes() {
|
public BookmarkType[] getBookmarkTypes() {
|
||||||
Collection<? extends TraceBookmarkType> types = bookmarkManager.getDefinedBookmarkTypes();
|
Collection<? extends TraceBookmarkType> types = bookmarkManager.getDefinedBookmarkTypes();
|
||||||
|
|||||||
+1
-1
@@ -236,7 +236,7 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject
|
|||||||
@Override
|
@Override
|
||||||
public String[] getPath() {
|
public String[] getPath() {
|
||||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||||
checkIsValid();
|
refreshIfNeeded();
|
||||||
if (isGlobal()) {
|
if (isGlobal()) {
|
||||||
return new String[] { getName() };
|
return new String[] { getName() };
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -89,7 +89,7 @@ public abstract class AbstractDBTraceSymbolSingleTypeWithLocationView<T extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void loadRangeCache(TraceAddressSnapRange range) {
|
protected synchronized void loadRangeCache(TraceAddressSnapRange range) {
|
||||||
rangeCache.clear();
|
rangeCache.clear();
|
||||||
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> idSpace =
|
DBTraceAddressSnapRangePropertyMapSpace<Long, DBTraceSymbolIDEntry> idSpace =
|
||||||
manager.idMap.getForSpace(range.getRange().getAddressSpace(), false);
|
manager.idMap.getForSpace(range.getRange().getAddressSpace(), false);
|
||||||
@@ -116,7 +116,7 @@ public abstract class AbstractDBTraceSymbolSingleTypeWithLocationView<T extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Collection<? extends T> doGetContaining(GetSymbolsKey key) {
|
protected synchronized Collection<? extends T> doGetContaining(GetSymbolsKey key) {
|
||||||
if (key.thread != null) {
|
if (key.thread != null) {
|
||||||
List<T> result =
|
List<T> result =
|
||||||
new ArrayList<>(getIntersecting(Lifespan.at(key.snap),
|
new ArrayList<>(getIntersecting(Lifespan.at(key.snap),
|
||||||
|
|||||||
+2
-2
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.trace.database.map;
|
package ghidra.trace.database.map;
|
||||||
|
|
||||||
import static ghidra.lifecycle.Unfinished.TODO;
|
import static ghidra.lifecycle.Unfinished.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -115,7 +115,7 @@ public class DBTraceAddressSnapRangePropertyMapSpaceTest
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearCache(boolean all) {
|
protected void clearCache(boolean all) {
|
||||||
try (LockHold hold = LockHold.lock(rwLock.writeLock())) {
|
try (LockHold hold = LockHold.lock(lock.writeLock())) {
|
||||||
// TODO: Should each space have an invalidateCache method?
|
// TODO: Should each space have an invalidateCache method?
|
||||||
super.clearCache(all);
|
super.clearCache(all);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.util;
|
|
||||||
|
|
||||||
public class GhidraLockHold implements AutoCloseable {
|
|
||||||
public static GhidraLockHold lock(Lock lock) {
|
|
||||||
GhidraLockHold hold = new GhidraLockHold(lock);
|
|
||||||
hold.lock.acquire();
|
|
||||||
return hold;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Lock lock;
|
|
||||||
|
|
||||||
protected GhidraLockHold(Lock lock) {
|
|
||||||
this.lock = lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
this.lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-3
@@ -19,7 +19,7 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec;
|
import ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec;
|
||||||
import ghidra.util.database.annot.DBAnnotatedObjectInfo;
|
import ghidra.util.database.annot.DBAnnotatedObjectInfo;
|
||||||
@@ -116,7 +116,7 @@ import ghidra.util.database.annot.DBAnnotatedObjectInfo;
|
|||||||
* Note that there is no way to specify the primary key. For object stores, the primary key is
|
* Note that there is no way to specify the primary key. For object stores, the primary key is
|
||||||
* always the object id, and its type is always {@code long}.
|
* always the object id, and its type is always {@code long}.
|
||||||
*/
|
*/
|
||||||
public class DBAnnotatedObject extends DatabaseObject {
|
public class DBAnnotatedObject extends DbObject {
|
||||||
private final DBCachedObjectStore<?> store;
|
private final DBCachedObjectStore<?> store;
|
||||||
private final DBCachedDomainObjectAdapter adapter;
|
private final DBCachedDomainObjectAdapter adapter;
|
||||||
private final List<DBFieldCodec<?, ?, ?>> codecs; // The codecs, ordered by field
|
private final List<DBFieldCodec<?, ?, ?>> codecs; // The codecs, ordered by field
|
||||||
@@ -131,7 +131,7 @@ public class DBAnnotatedObject extends DatabaseObject {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
protected DBAnnotatedObject(DBCachedObjectStore<?> store, DBRecord record) {
|
protected DBAnnotatedObject(DBCachedObjectStore<?> store, DBRecord record) {
|
||||||
super(store == null ? null : store.cache, record == null ? -1 : record.getKey());
|
super(record == null ? -1 : record.getKey());
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.record = record;
|
this.record = record;
|
||||||
if (store != null) {
|
if (store != null) {
|
||||||
|
|||||||
+4
-83
@@ -15,14 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.util.database;
|
package ghidra.util.database;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.concurrent.locks.*;
|
|
||||||
|
|
||||||
import db.DBHandle;
|
import db.DBHandle;
|
||||||
import ghidra.framework.data.DBDomainObjectSupport;
|
import ghidra.framework.data.DBDomainObjectSupport;
|
||||||
import ghidra.framework.data.OpenMode;
|
import ghidra.framework.data.OpenMode;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Swing;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,89 +83,12 @@ public abstract class DBCachedDomainObjectAdapter extends DBDomainObjectSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapts a {@link ghidra.util.Lock} to the {@link Lock} interface
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Not all operations are supported. In particular, no {@link #lockInterruptibly()},
|
|
||||||
* {@link #tryLock(long,TimeUnit)}, nor {@link #newCondition()}.
|
|
||||||
*/
|
|
||||||
static class GhidraLockWrappingLock implements Lock {
|
|
||||||
private final ghidra.util.Lock ghidraLock;
|
|
||||||
|
|
||||||
public GhidraLockWrappingLock(ghidra.util.Lock ghidraLock) {
|
|
||||||
this.ghidraLock = ghidraLock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lock() {
|
|
||||||
ghidraLock.acquire();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lockInterruptibly() throws InterruptedException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock() {
|
|
||||||
synchronized (ghidraLock) { // Yes, sync on the lock's intrinsic lock
|
|
||||||
Thread lockOwner = ghidraLock.getOwner();
|
|
||||||
if (lockOwner == null || lockOwner == Thread.currentThread()) {
|
|
||||||
ghidraLock.acquire();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock() {
|
|
||||||
ghidraLock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Condition newCondition() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not a true read-write lock, but adapts a {@link ghidra.util.Lock} to the
|
|
||||||
* {@link ReadWriteLock} interface. The read lock and the write lock are just the same lock
|
|
||||||
*/
|
|
||||||
static class GhidraLockWrappingRWLock implements ReadWriteLock {
|
|
||||||
private final GhidraLockWrappingLock oneLock;
|
|
||||||
|
|
||||||
public GhidraLockWrappingRWLock(ghidra.util.Lock ghidraLock) {
|
|
||||||
this.oneLock = new GhidraLockWrappingLock(ghidraLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Lock readLock() {
|
|
||||||
return oneLock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Lock writeLock() {
|
|
||||||
return oneLock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ReadWriteLock rwLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see DBDomainObjectSupport
|
* @see DBDomainObjectSupport
|
||||||
*/
|
*/
|
||||||
protected DBCachedDomainObjectAdapter(DBHandle dbh, OpenMode openMode, TaskMonitor monitor,
|
protected DBCachedDomainObjectAdapter(DBHandle dbh, OpenMode openMode, TaskMonitor monitor,
|
||||||
String name, int timeInterval, int bufSize, Object consumer) {
|
String name, int timeInterval, int bufSize, Object consumer) {
|
||||||
super(dbh, openMode, monitor, name, timeInterval, bufSize, consumer);
|
super(dbh, openMode, monitor, name, timeInterval, bufSize, consumer);
|
||||||
this.rwLock = new GhidraLockWrappingRWLock(lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +96,7 @@ public abstract class DBCachedDomainObjectAdapter extends DBDomainObjectSupport
|
|||||||
*
|
*
|
||||||
* @return the lock
|
* @return the lock
|
||||||
*/
|
*/
|
||||||
public ReadWriteLock getReadWriteLock() {
|
public Lock getReadWriteLock() {
|
||||||
return rwLock;
|
return lock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-11
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -28,7 +28,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
|
|||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import db.util.ErrorHandler;
|
import db.util.ErrorHandler;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbCache;
|
||||||
import ghidra.program.model.address.KeyRange;
|
import ghidra.program.model.address.KeyRange;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec;
|
import ghidra.util.database.DBCachedObjectStoreFactory.DBFieldCodec;
|
||||||
@@ -41,7 +41,7 @@ import ghidra.util.database.annot.DBAnnotatedField;
|
|||||||
* <p>
|
* <p>
|
||||||
* Essentially, this provides object-based accessed to records in the table via DAOs. See
|
* Essentially, this provides object-based accessed to records in the table via DAOs. See
|
||||||
* {@link DBAnnotatedObject} for further documentation including an example object definition. The
|
* {@link DBAnnotatedObject} for further documentation including an example object definition. The
|
||||||
* store keeps a cache of objects using {@link DBObjectCache}. See
|
* store keeps a cache of objects using {@link DbCache}. See
|
||||||
* {@link DBCachedObjectStoreFactory} for documentation describing how to create a store, including
|
* {@link DBCachedObjectStoreFactory} for documentation describing how to create a store, including
|
||||||
* for the example object definition.
|
* for the example object definition.
|
||||||
*
|
*
|
||||||
@@ -200,7 +200,7 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
}
|
}
|
||||||
|
|
||||||
E get(long key) throws IOException {
|
E get(long key) throws IOException {
|
||||||
T cached = cache.get(key);
|
T cached = cache.getCachedInstance(key);
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
return fromObject(cached);
|
return fromObject(cached);
|
||||||
}
|
}
|
||||||
@@ -580,7 +580,7 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean typedContains(Long u) throws IOException {
|
boolean typedContains(Long u) throws IOException {
|
||||||
if (cache.get(u) != null) {
|
if (cache.getCachedInstance(u) != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return table.hasRecord(u);
|
return table.hasRecord(u);
|
||||||
@@ -619,11 +619,12 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
if (record == null) {
|
if (record == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
T cached = cache.get(record);
|
T cached = cache.getCachedInstance(record);
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
T found = factory.create(DBCachedObjectStore.this, record);
|
T found = factory.create(DBCachedObjectStore.this, record);
|
||||||
|
cache.add(found);
|
||||||
found.doRefresh(record);
|
found.doRefresh(record);
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
@@ -747,7 +748,7 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
|
|
||||||
final DBCachedDomainObjectAdapter adapter;
|
final DBCachedDomainObjectAdapter adapter;
|
||||||
final DBHandle dbh;
|
final DBHandle dbh;
|
||||||
final DBObjectCache<T> cache;
|
final DbCache<T> cache;
|
||||||
private final Class<T> objectType;
|
private final Class<T> objectType;
|
||||||
private final DBAnnotatedObjectFactory<T> factory;
|
private final DBAnnotatedObjectFactory<T> factory;
|
||||||
private final String tableName;
|
private final String tableName;
|
||||||
@@ -783,8 +784,9 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
this.table = table;
|
this.table = table;
|
||||||
this.tableName = table.getName();
|
this.tableName = table.getName();
|
||||||
this.schema = table.getSchema();
|
this.schema = table.getSchema();
|
||||||
this.cache = new DBObjectCache<>(1000); // TODO: Parameterize this?
|
|
||||||
this.lock = adapter.getReadWriteLock();
|
this.lock = adapter.getReadWriteLock();
|
||||||
|
this.cache = new DbCache<T>(new DebuggerFactoryAdapter<>(this, table, factory),
|
||||||
|
adapter.getReadWriteLock(), 1000);
|
||||||
this.codecs = DBCachedObjectStoreFactory.getCodecs(objectType);
|
this.codecs = DBCachedObjectStoreFactory.getCodecs(objectType);
|
||||||
|
|
||||||
this.asForwardMap = new DBCachedObjectStoreMap<>(this, adapter, lock, Direction.FORWARD);
|
this.asForwardMap = new DBCachedObjectStoreMap<>(this, adapter, lock, Direction.FORWARD);
|
||||||
@@ -923,11 +925,12 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected T doCreate(long key) throws IOException {
|
private T doCreate(long key) throws IOException {
|
||||||
DBRecord rec = schema.createRecord(key);
|
DBRecord rec = schema.createRecord(key);
|
||||||
table.putRecord(rec);
|
table.putRecord(rec);
|
||||||
T created = factory.create(this, rec);
|
T created = factory.create(this, rec);
|
||||||
created.fresh(true);
|
created.fresh(true);
|
||||||
|
cache.add(created);
|
||||||
created.doUpdateAll();
|
created.doUpdateAll();
|
||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
@@ -1224,7 +1227,7 @@ public class DBCachedObjectStore<T extends DBAnnotatedObject> implements ErrorHa
|
|||||||
* @return true if cached
|
* @return true if cached
|
||||||
*/
|
*/
|
||||||
boolean isCached(long key) {
|
boolean isCached(long key) {
|
||||||
return cache.get(key) != null;
|
return cache.getCachedInstance(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.util.database;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import db.DBRecord;
|
||||||
|
import db.Table;
|
||||||
|
import ghidra.program.database.DbCache;
|
||||||
|
import ghidra.program.database.DbFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps {@link DBAnnotatedObjectFactory} objects into {@link DbFactory} objects that is needed by
|
||||||
|
* the {@link DbCache}.
|
||||||
|
*
|
||||||
|
* @param <T> The DBAnnotatedObject type
|
||||||
|
*/
|
||||||
|
public class DebuggerFactoryAdapter<T extends DBAnnotatedObject> implements DbFactory<T> {
|
||||||
|
|
||||||
|
private DBCachedObjectStore<T> store;
|
||||||
|
private Table table;
|
||||||
|
private DBAnnotatedObjectFactory<T> factory;
|
||||||
|
|
||||||
|
public DebuggerFactoryAdapter(DBCachedObjectStore<T> store, Table table,
|
||||||
|
DBAnnotatedObjectFactory<T> factory) {
|
||||||
|
this.store = store;
|
||||||
|
this.table = table;
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T instantiate(long key) {
|
||||||
|
try {
|
||||||
|
DBRecord record = table.getRecord(key);
|
||||||
|
return instantiate(record);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
store.dbError(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T instantiate(DBRecord record) {
|
||||||
|
if (record == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
T t = factory.create(store, record);
|
||||||
|
t.refresh(record);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+23
-19
@@ -874,27 +874,31 @@ public abstract class AbstractConstraintsTree<
|
|||||||
* {@link #checkDataIntegrity(DBTreeDataRecord)} instead of this method.
|
* {@link #checkDataIntegrity(DBTreeDataRecord)} instead of this method.
|
||||||
*/
|
*/
|
||||||
public void checkIntegrity() {
|
public void checkIntegrity() {
|
||||||
// Before we visit, integrity check that cache. Visiting will affect cache.
|
synchronized (cachedDataChildren) {
|
||||||
for (Entry<Long, Collection<DR>> ent : cachedDataChildren.entrySet()) {
|
// Before we visit, integrity check that cache. Visiting will affect cache.
|
||||||
Set<DR> databasedChildren = new TreeSet<>(Comparator.comparing(DR::getKey));
|
for (Entry<Long, Collection<DR>> ent : cachedDataChildren.entrySet()) {
|
||||||
// NOTE: Bypass the cache by using the variant with a key parameter
|
Set<DR> databasedChildren = new TreeSet<>(Comparator.comparing(DR::getKey));
|
||||||
databasedChildren.addAll(getDataChildrenOf(ent.getKey()));
|
// NOTE: Bypass the cache by using the variant with a key parameter
|
||||||
Set<DR> cachedChildren = new TreeSet<>(Comparator.comparing(DR::getKey));
|
databasedChildren.addAll(getDataChildrenOf(ent.getKey()));
|
||||||
cachedChildren.addAll(ent.getValue());
|
Set<DR> cachedChildren = new TreeSet<>(Comparator.comparing(DR::getKey));
|
||||||
if (!databasedChildren.equals(cachedChildren)) {
|
cachedChildren.addAll(ent.getValue());
|
||||||
throw new AssertionError("Cached children of node " + ent.getKey() +
|
if (!databasedChildren.equals(cachedChildren)) {
|
||||||
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
throw new AssertionError("Cached children of node " + ent.getKey() +
|
||||||
|
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Entry<Long, Collection<NR>> ent : cachedNodeChildren.entrySet()) {
|
synchronized (cachedNodeChildren) {
|
||||||
Set<NR> databasedChildren = new TreeSet<>(Comparator.comparing(NR::getKey));
|
for (Entry<Long, Collection<NR>> ent : cachedNodeChildren.entrySet()) {
|
||||||
// NOTE: Bypass the cache by using the variant with a key parameter
|
Set<NR> databasedChildren = new TreeSet<>(Comparator.comparing(NR::getKey));
|
||||||
databasedChildren.addAll(getNodeChildrenOf(ent.getKey()));
|
// NOTE: Bypass the cache by using the variant with a key parameter
|
||||||
Set<NR> cachedChildren = new TreeSet<>(Comparator.comparing(NR::getKey));
|
databasedChildren.addAll(getNodeChildrenOf(ent.getKey()));
|
||||||
cachedChildren.addAll(ent.getValue());
|
Set<NR> cachedChildren = new TreeSet<>(Comparator.comparing(NR::getKey));
|
||||||
if (!databasedChildren.equals(cachedChildren)) {
|
cachedChildren.addAll(ent.getValue());
|
||||||
throw new AssertionError("Cached children of node " + ent.getKey() +
|
if (!databasedChildren.equals(cachedChildren)) {
|
||||||
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
throw new AssertionError("Cached children of node " + ent.getKey() +
|
||||||
|
" out of sync: cache=" + cachedChildren + " db=" + databasedChildren);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
visit(null, new TreeRecordVisitor() {
|
visit(null, new TreeRecordVisitor() {
|
||||||
|
|||||||
-9
@@ -43,7 +43,6 @@ public class BookmarkNavigator {
|
|||||||
final static Icon WARNING_ICON = new GIcon("icon.plugin.bookmark.type.warning");
|
final static Icon WARNING_ICON = new GIcon("icon.plugin.bookmark.type.warning");
|
||||||
final static Icon ERROR_ICON = new GIcon("icon.plugin.bookmark.type.error");
|
final static Icon ERROR_ICON = new GIcon("icon.plugin.bookmark.type.error");
|
||||||
final static Icon ANALYSIS_ICON = new GIcon("icon.plugin.bookmark.type.analysis");
|
final static Icon ANALYSIS_ICON = new GIcon("icon.plugin.bookmark.type.analysis");
|
||||||
final static Icon DEFAULT_ICON = new GIcon("icon.plugin.bookmark.type.default");
|
|
||||||
|
|
||||||
final static int NOTE_PRIORITY = MarkerService.BOOKMARK_PRIORITY;
|
final static int NOTE_PRIORITY = MarkerService.BOOKMARK_PRIORITY;
|
||||||
final static int ERROR_PRIORITY = MarkerService.BOOKMARK_PRIORITY + BIG_CHANGE;
|
final static int ERROR_PRIORITY = MarkerService.BOOKMARK_PRIORITY + BIG_CHANGE;
|
||||||
@@ -81,14 +80,6 @@ public class BookmarkNavigator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Icon icon = bmt.getIcon();
|
Icon icon = bmt.getIcon();
|
||||||
if (icon == null) {
|
|
||||||
if (bookmarkManager.isDefinedType(type)) {
|
|
||||||
// This implies the client defined a type, but did not pass a valid icon. In this
|
|
||||||
// case we will show a special icon.
|
|
||||||
icon = DEFAULT_ICON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Color color = bmt.getMarkerColor();
|
Color color = bmt.getMarkerColor();
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
color = DEFAULT_COLOR;
|
color = DEFAULT_COLOR;
|
||||||
|
|||||||
+3
-3
@@ -21,7 +21,7 @@ import javax.help.UnsupportedOperationException;
|
|||||||
|
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.fieldpanel.support.*;
|
import docking.widgets.fieldpanel.support.*;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.InsufficientBytesException;
|
import ghidra.program.model.lang.InsufficientBytesException;
|
||||||
@@ -1443,7 +1443,7 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
|
|||||||
else {
|
else {
|
||||||
// Check for managed datatype changing
|
// Check for managed datatype changing
|
||||||
DataType originalDt = originalDTM.getDataType(newPath);
|
DataType originalDt = originalDTM.getDataType(newPath);
|
||||||
if (!(originalDt instanceof DatabaseObject)) {
|
if (!(originalDt instanceof DbObject)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
|
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
|
||||||
@@ -1532,7 +1532,7 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
|
|||||||
// undo transactions for the viewDTM. An editor save could generate quite a few with
|
// undo transactions for the viewDTM. An editor save could generate quite a few with
|
||||||
// potentially many types getting changed by one change.
|
// potentially many types getting changed by one change.
|
||||||
DataType changedDt = originalDTM.getDataType(path);
|
DataType changedDt = originalDTM.getDataType(path);
|
||||||
if (!(changedDt instanceof DatabaseObject)) {
|
if (!(changedDt instanceof DbObject)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataType viewDt =
|
DataType viewDt =
|
||||||
|
|||||||
+8
-8
@@ -22,7 +22,7 @@ import java.util.TreeSet;
|
|||||||
import javax.help.UnsupportedOperationException;
|
import javax.help.UnsupportedOperationException;
|
||||||
|
|
||||||
import db.util.ErrorHandler;
|
import db.util.ErrorHandler;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.ProgramArchitecture;
|
import ghidra.program.model.lang.ProgramArchitecture;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
@@ -231,7 +231,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
return viewComposite;
|
return viewComposite;
|
||||||
}
|
}
|
||||||
DataType resolvedDt = super.resolve(dataType, handler);
|
DataType resolvedDt = super.resolve(dataType, handler);
|
||||||
if ((dataType instanceof DatabaseObject) && originalDTM.contains(dataType)) {
|
if ((dataType instanceof DbObject) && originalDTM.contains(dataType)) {
|
||||||
long originalId = originalDTM.getID(dataType);
|
long originalId = originalDTM.getID(dataType);
|
||||||
long myId = getID(resolvedDt);
|
long myId = getID(resolvedDt);
|
||||||
dataTypeIDMap.put(myId, originalId);
|
dataTypeIDMap.put(myId, originalId);
|
||||||
@@ -247,14 +247,14 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
throw new IllegalArgumentException("datatype is not from this manager");
|
throw new IllegalArgumentException("datatype is not from this manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingViewDt instanceof DatabaseObject) {
|
if (existingViewDt instanceof DbObject) {
|
||||||
dataTypeIDMap.remove(getID(existingViewDt));
|
dataTypeIDMap.remove(getID(existingViewDt));
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType newResolvedDt =
|
DataType newResolvedDt =
|
||||||
super.replaceDataType(existingViewDt, replacementDt, updateCategoryPath);
|
super.replaceDataType(existingViewDt, replacementDt, updateCategoryPath);
|
||||||
|
|
||||||
if (newResolvedDt instanceof DatabaseObject &&
|
if (newResolvedDt instanceof DbObject &&
|
||||||
replacementDt.getDataTypeManager() == originalDTM) {
|
replacementDt.getDataTypeManager() == originalDTM) {
|
||||||
long originalId = originalDTM.getID(replacementDt);
|
long originalId = originalDTM.getID(replacementDt);
|
||||||
long myId = getID(newResolvedDt);
|
long myId = getID(newResolvedDt);
|
||||||
@@ -271,7 +271,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
throw new IllegalArgumentException("datatype is not from this manager");
|
throw new IllegalArgumentException("datatype is not from this manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingViewDt instanceof DatabaseObject) {
|
if (existingViewDt instanceof DbObject) {
|
||||||
dataTypeIDMap.remove(getID(existingViewDt));
|
dataTypeIDMap.remove(getID(existingViewDt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
Iterator<DataType> allDataTypes = getAllDataTypes();
|
Iterator<DataType> allDataTypes = getAllDataTypes();
|
||||||
while (allDataTypes.hasNext()) {
|
while (allDataTypes.hasNext()) {
|
||||||
DataType dt = allDataTypes.next();
|
DataType dt = allDataTypes.next();
|
||||||
if (dt == viewComposite || !(dt instanceof DatabaseObject)) {
|
if (dt == viewComposite || !(dt instanceof DbObject)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +396,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
long id = orphanIds.removeFirst();
|
long id = orphanIds.removeFirst();
|
||||||
if (!hasParent(id)) {
|
if (!hasParent(id)) {
|
||||||
DataType dt = getDataType(id);
|
DataType dt = getDataType(id);
|
||||||
if (dt instanceof DatabaseObject) {
|
if (dt instanceof DbObject) {
|
||||||
|
|
||||||
if (dt == viewComposite) {
|
if (dt == viewComposite) {
|
||||||
continue;
|
continue;
|
||||||
@@ -485,7 +485,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
|
|||||||
if (existingViewDt.getDataTypeManager() != this) {
|
if (existingViewDt.getDataTypeManager() != this) {
|
||||||
throw new IllegalArgumentException("datatype is not from this manager");
|
throw new IllegalArgumentException("datatype is not from this manager");
|
||||||
}
|
}
|
||||||
if (!(existingViewDt instanceof DatabaseObject)) {
|
if (!(existingViewDt instanceof DbObject)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return dataTypeIDMap.getOriginalIDFromViewID(getID(existingViewDt)) != null;
|
return dataTypeIDMap.getOriginalIDFromViewID(getID(existingViewDt)) != null;
|
||||||
|
|||||||
+4
-4
@@ -36,7 +36,7 @@ import ghidra.app.plugin.core.stackeditor.StackFrameDataType.StackComponentWrapp
|
|||||||
import ghidra.app.util.datatype.EmptyCompositeException;
|
import ghidra.app.util.datatype.EmptyCompositeException;
|
||||||
import ghidra.framework.plugintool.Plugin;
|
import ghidra.framework.plugintool.Plugin;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
@@ -1125,7 +1125,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
|
|||||||
|
|
||||||
// Check for managed datatype changing
|
// Check for managed datatype changing
|
||||||
DataType originalDt = originalDTM.getDataType(newPath);
|
DataType originalDt = originalDTM.getDataType(newPath);
|
||||||
if (!(originalDt instanceof DatabaseObject)) {
|
if (!(originalDt instanceof DbObject)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
|
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
|
||||||
@@ -1161,7 +1161,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DataType changedDt = originalDTM.getDataType(path);
|
DataType changedDt = originalDTM.getDataType(path);
|
||||||
if (!(changedDt instanceof DatabaseObject)) {
|
if (!(changedDt instanceof DbObject)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataType viewDt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(changedDt));
|
DataType viewDt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(changedDt));
|
||||||
@@ -1250,7 +1250,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
|
|||||||
for (int i = comps.length - 1; i >= 0; i--) {
|
for (int i = comps.length - 1; i >= 0; i--) {
|
||||||
DataTypeComponent component = comps[i];
|
DataTypeComponent component = comps[i];
|
||||||
DataType compDt = component.getDataType();
|
DataType compDt = component.getDataType();
|
||||||
if (compDt instanceof DatabaseObject) {
|
if (compDt instanceof DbObject) {
|
||||||
// NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
|
// NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
|
||||||
long myId = viewDTM.getID(compDt);
|
long myId = viewDTM.getID(compDt);
|
||||||
if (viewDTM.findOriginalDataTypeFromMyID(myId) == null) {
|
if (viewDTM.findOriginalDataTypeFromMyID(myId) == null) {
|
||||||
|
|||||||
+2
-2
@@ -28,7 +28,7 @@ import ghidra.app.util.DataTypeNamingUtil;
|
|||||||
import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
|
import ghidra.app.util.bin.format.dwarf.attribs.DWARFNumericAttribute;
|
||||||
import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
|
import ghidra.app.util.bin.format.dwarf.expression.DWARFExpressionException;
|
||||||
import ghidra.app.util.bin.format.golang.rtti.types.GoKind;
|
import ghidra.app.util.bin.format.golang.rtti.types.GoKind;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
@@ -247,7 +247,7 @@ public class DWARFDataTypeImporter {
|
|||||||
* offset -> ddt
|
* offset -> ddt
|
||||||
*/
|
*/
|
||||||
private void recordTempDataType(DWARFDataType ddt) {
|
private void recordTempDataType(DWARFDataType ddt) {
|
||||||
if (ddt.dataType instanceof DatabaseObject) {
|
if (ddt.dataType instanceof DbObject) {
|
||||||
// don't store info about types that are already in the database
|
// don't store info about types that are already in the database
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -455,7 +455,6 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||||||
if (separator != null) {
|
if (separator != null) {
|
||||||
results.add(separator);
|
results.add(separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int opIndex = 0; opIndex < numOperands; opIndex++) {
|
for (int opIndex = 0; opIndex < numOperands; opIndex++) {
|
||||||
OpInfo opInfo = new OpInfo(inst, opIndex);
|
OpInfo opInfo = new OpInfo(inst, opIndex);
|
||||||
addOperandElements(opInfo, results);
|
addOperandElements(opInfo, results);
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ import javax.swing.*;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
|
||||||
public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTest {
|
public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTest {
|
||||||
@@ -312,7 +312,7 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes
|
|||||||
private DataTypeComponent addFlexDataType(Structure struct, DataType dataType, String name,
|
private DataTypeComponent addFlexDataType(Structure struct, DataType dataType, String name,
|
||||||
String comment) {
|
String comment) {
|
||||||
ArrayDataType a = new ArrayDataType(dataType, 0, 1);
|
ArrayDataType a = new ArrayDataType(dataType, 0, 1);
|
||||||
if (struct instanceof DatabaseObject) {
|
if (struct instanceof DbObject) {
|
||||||
DataTypeManager dtm = struct.getDataTypeManager();
|
DataTypeManager dtm = struct.getDataTypeManager();
|
||||||
return dtm.withTransaction("Add Flex Array", () -> struct.add(a, name, comment));
|
return dtm.withTransaction("Add Flex Array", () -> struct.add(a, name, comment));
|
||||||
}
|
}
|
||||||
|
|||||||
+122
-51
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -17,31 +17,37 @@ package ghidra.program.database;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.ConcurrentModificationException;
|
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import db.*;
|
||||||
import ghidra.program.model.address.KeyRange;
|
import ghidra.program.model.address.KeyRange;
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
import ghidra.util.Lock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
private DBObjectCache<TestObj> cache = new DBObjectCache<TestObj>(100);
|
private DbCache<TestObj> cache;
|
||||||
|
private Lock lock = new Lock("test");
|
||||||
|
private Schema schema;
|
||||||
|
private Map<Long, DBRecord> database = new HashMap<>();
|
||||||
|
|
||||||
/**
|
@Before
|
||||||
* Constructor for ObjectCacheTest.
|
public void setUp() {
|
||||||
* @param arg0
|
Field[] fields = { new IntField() };
|
||||||
*/
|
String[] fieldNames = { "Value" };
|
||||||
public ObjectCacheTest() {
|
|
||||||
super();
|
schema = new Schema(1, "id", fields, fieldNames);
|
||||||
|
cache = new DbCache<TestObj>(new TestFactory(), lock, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetInvalid() {
|
public void testDeleted() {
|
||||||
TestObj obj1 = getTestObj(1);
|
TestObj obj1 = createTestObj(1, 1);
|
||||||
obj1.setInvalid();
|
deleteTestObj(obj1);
|
||||||
try {
|
try {
|
||||||
obj1.setValue(10);
|
obj1.setValue(10);
|
||||||
fail("Should have thrown a concurrent modification excption here.");
|
fail("Should have thrown a concurrent modification excption here.");
|
||||||
@@ -53,8 +59,10 @@ public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteFromCacheSetsDeletedOnObject() {
|
public void testDeleteFromCacheSetsDeletedOnObject() {
|
||||||
TestObj obj1 = getTestObj(1);
|
TestObj obj1 = createTestObj(1, 1);
|
||||||
cache.delete(1);
|
cache.delete(1);
|
||||||
|
assertFalse(obj1.isValid());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
obj1.setValue(10);
|
obj1.setValue(10);
|
||||||
fail("Should have thrown a concurrent modification excption here.");
|
fail("Should have thrown a concurrent modification excption here.");
|
||||||
@@ -66,11 +74,12 @@ public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteUndoProblem() {
|
public void testDeleteUndoProblem() {
|
||||||
TestObj obj1 = getTestObj(1);
|
TestObj obj1 = createTestObj(1, 1);
|
||||||
obj1.setInvalid();
|
deleteTestObj(obj1);
|
||||||
obj1.checkIsValid();
|
assertFalse(obj1.refreshIfNeeded());
|
||||||
|
assertTrue(obj1.isDeleted(lock));
|
||||||
|
|
||||||
TestObj obj2 = getTestObj(1);
|
TestObj obj2 = createTestObj(1, 1);
|
||||||
assertTrue(obj1 != obj2);
|
assertTrue(obj1 != obj2);
|
||||||
|
|
||||||
obj1.getValue(); // previously, this would have caused obj2 to be removed
|
obj1.getValue(); // previously, this would have caused obj2 to be removed
|
||||||
@@ -84,7 +93,7 @@ public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testDeleteRange() {
|
public void testDeleteRange() {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
getTestObj(i);
|
createTestObj(i, i);
|
||||||
}
|
}
|
||||||
assertEquals(10, cache.size());
|
assertEquals(10, cache.size());
|
||||||
cache.delete(Arrays.asList(new KeyRange(4, 6)));
|
cache.delete(Arrays.asList(new KeyRange(4, 6)));
|
||||||
@@ -94,43 +103,105 @@ public class ObjectCacheTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testDeleteBigRange() {
|
public void testDeleteBigRange() {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
getTestObj(i);
|
createTestObj(i, i);
|
||||||
}
|
}
|
||||||
assertEquals(10, cache.size());
|
assertEquals(10, cache.size());
|
||||||
cache.delete(Arrays.asList(new KeyRange(2, 100)));
|
cache.delete(Arrays.asList(new KeyRange(2, 100)));
|
||||||
assertEquals(2, cache.size());
|
assertEquals(2, cache.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestObj getTestObj(int key) {
|
@Test
|
||||||
TestObj obj = cache.get(key);
|
public void testRefresh() {
|
||||||
if (obj == null) {
|
TestObj obj1 = createTestObj(1, 1);
|
||||||
obj = new TestObj(cache, key);
|
obj1.setInvalid();
|
||||||
|
assertFalse(obj1.isValid());
|
||||||
|
assertEquals(1, obj1.getValue());
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalideCache() {
|
||||||
|
TestObj obj1 = createTestObj(1, 1);
|
||||||
|
TestObj obj2 = createTestObj(2, 2);
|
||||||
|
TestObj obj3 = createTestObj(3, 3);
|
||||||
|
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
|
||||||
|
cache.invalidate();
|
||||||
|
|
||||||
|
assertFalse(obj1.isValid());
|
||||||
|
assertFalse(obj1.isValid());
|
||||||
|
assertFalse(obj1.isValid());
|
||||||
|
|
||||||
|
assertTrue(obj1 == getTestObj(1));
|
||||||
|
assertTrue(obj2 == getTestObj(2));
|
||||||
|
assertTrue(obj3 == getTestObj(3));
|
||||||
|
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
assertTrue(obj1.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestObj createTestObj(long key, int value) {
|
||||||
|
DBRecord record = schema.createRecord(key);
|
||||||
|
record.setIntValue(0, value);
|
||||||
|
database.put(key, record);
|
||||||
|
return cache.getCachedInstance(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestObj getTestObj(long key) {
|
||||||
|
return cache.getCachedInstance(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteTestObj(TestObj obj) {
|
||||||
|
database.remove(obj.getKey());
|
||||||
|
obj.setInvalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestFactory implements DbFactory<TestObj> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestObj instantiate(long key) {
|
||||||
|
DBRecord record = database.get(key);
|
||||||
|
return instantiate(record);
|
||||||
}
|
}
|
||||||
return obj;
|
|
||||||
|
@Override
|
||||||
|
public TestObj instantiate(DBRecord record) {
|
||||||
|
return new TestObj(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestObj extends DbObject {
|
||||||
|
private DBRecord record;
|
||||||
|
|
||||||
|
TestObj(DBRecord record) {
|
||||||
|
super(record.getKey());
|
||||||
|
this.record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value) {
|
||||||
|
checkDeleted();
|
||||||
|
record.setIntValue(0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
refreshIfNeeded();
|
||||||
|
return record.getIntValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean refresh() {
|
||||||
|
DBRecord refreshedRecord = database.get(record.getKey());
|
||||||
|
if (refreshedRecord != null) {
|
||||||
|
record = refreshedRecord;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestObj extends DatabaseObject {
|
|
||||||
int value = -1;
|
|
||||||
|
|
||||||
TestObj(DBObjectCache<TestObj> cache, long key) {
|
|
||||||
super(cache, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(int value) {
|
|
||||||
checkDeleted();
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValue() {
|
|
||||||
checkIsValid();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean refresh() {
|
|
||||||
// return false to simulate the record has been deleted
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
+13
-12
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -20,7 +20,7 @@ import static org.junit.Assert.*;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.data.PointerTypedefInspector;
|
import ghidra.program.database.data.PointerTypedefInspector;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
@@ -96,7 +96,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals("char * " + formatAttributes("offset(0x8)"), dt.getName());
|
assertEquals("char * " + formatAttributes("offset(0x8)"), dt.getName());
|
||||||
DataType dbDt = dtm.resolve(dt, null);
|
DataType dbDt = dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof TypeDef);
|
assertTrue(dbDt instanceof TypeDef);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals("char * " + formatAttributes("relative"), dt.getName());
|
assertEquals("char * " + formatAttributes("relative"), dt.getName());
|
||||||
dbDt = dtm.resolve(dt, null);
|
dbDt = dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof TypeDef);
|
assertTrue(dbDt instanceof TypeDef);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals("char *16 " + formatAttributes("space(register)"), dt.getName());
|
assertEquals("char *16 " + formatAttributes("space(register)"), dt.getName());
|
||||||
DataType dbDt = dtm.resolve(dt, null);
|
DataType dbDt = dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof TypeDef);
|
assertTrue(dbDt instanceof TypeDef);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals("char *32 " + formatAttributes("space(register)"), dt.getName());
|
assertEquals("char *32 " + formatAttributes("space(register)"), dt.getName());
|
||||||
dbDt = dtm.resolve(dt, null);
|
dbDt = dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof TypeDef);
|
assertTrue(dbDt instanceof TypeDef);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
||||||
DataType dbDt = dtm.resolve(dt, null);
|
DataType dbDt = dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof TypeDef);
|
assertTrue(dbDt instanceof TypeDef);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals(2, dt.getLength());
|
assertEquals(2, dt.getLength());
|
||||||
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
||||||
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
|
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -252,7 +252,8 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertTrue(dbDt == dtm.resolve(dt, null)); // should resolve to same instance
|
assertTrue(dbDt == dtm.resolve(dt, null)); // should resolve to same instance
|
||||||
|
|
||||||
dt = (TypeDef) dt.copy(dtm);
|
dt = (TypeDef) dt.copy(dtm);
|
||||||
PointerTypeSettingsDefinition.DEF.setType(dt.getDefaultSettings(), PointerType.IMAGE_BASE_RELATIVE);
|
PointerTypeSettingsDefinition.DEF.setType(dt.getDefaultSettings(),
|
||||||
|
PointerType.IMAGE_BASE_RELATIVE);
|
||||||
TypeDef dbDt2 = (TypeDef) dtm.resolve(dt, null); // should resolve to new instance
|
TypeDef dbDt2 = (TypeDef) dtm.resolve(dt, null); // should resolve to new instance
|
||||||
assertTrue(dbDt != dbDt2);
|
assertTrue(dbDt != dbDt2);
|
||||||
assertEquals("foo *16 " + formatAttributes("image-base-relative,space(register)"),
|
assertEquals("foo *16 " + formatAttributes("image-base-relative,space(register)"),
|
||||||
@@ -276,7 +277,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertEquals(2, dt.getLength());
|
assertEquals(2, dt.getLength());
|
||||||
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
|
||||||
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
|
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
|
||||||
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt.isEquivalent(dbDt));
|
assertTrue(dt.isEquivalent(dbDt));
|
||||||
assertEquals(dt.getName(), dbDt.getName());
|
assertEquals(dt.getName(), dbDt.getName());
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
|
|||||||
assertTrue(dbDt != dbDt2);
|
assertTrue(dbDt != dbDt2);
|
||||||
assertFalse(dbDt.isEquivalent(dbDt2));
|
assertFalse(dbDt.isEquivalent(dbDt2));
|
||||||
assertFalse(dbDt2.isEquivalent(dbDt));
|
assertFalse(dbDt2.isEquivalent(dbDt));
|
||||||
assertTrue(dbDt2 instanceof DatabaseObject); // transforms to TypedefDB
|
assertTrue(dbDt2 instanceof DbObject); // transforms to TypedefDB
|
||||||
assertTrue(dt2.isEquivalent(dbDt2));
|
assertTrue(dt2.isEquivalent(dbDt2));
|
||||||
assertEquals(dt2.getName(), dbDt2.getName());
|
assertEquals(dt2.getName(), dbDt2.getName());
|
||||||
|
|
||||||
|
|||||||
+3
-11
@@ -37,6 +37,7 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
|||||||
import ghidra.program.model.util.PropertyMap;
|
import ghidra.program.model.util.PropertyMap;
|
||||||
import ghidra.test.ToyProgramBuilder;
|
import ghidra.test.ToyProgramBuilder;
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.SaveableColor;
|
import ghidra.util.SaveableColor;
|
||||||
import ghidra.util.exception.NoValueException;
|
import ghidra.util.exception.NoValueException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -992,8 +993,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||||||
ProgramDB pdb = (ProgramDB) program;
|
ProgramDB pdb = (ProgramDB) program;
|
||||||
Lock lock = pdb.getLock();
|
Lock lock = pdb.getLock();
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
|
|
||||||
Instruction instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
Instruction instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
||||||
assertEquals(FlowOverride.NONE, instructionAt.getFlowOverride());
|
assertEquals(FlowOverride.NONE, instructionAt.getFlowOverride());
|
||||||
@@ -1007,9 +1007,6 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||||||
instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
||||||
assertEquals(FlowOverride.CALL, instructionAt.getFlowOverride());
|
assertEquals(FlowOverride.CALL, instructionAt.getFlowOverride());
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1020,9 +1017,7 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||||||
ProgramDB pdb = (ProgramDB) program;
|
ProgramDB pdb = (ProgramDB) program;
|
||||||
Lock lock = pdb.getLock();
|
Lock lock = pdb.getLock();
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
|
|
||||||
Instruction instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
Instruction instructionAt = pdb.getListing().getInstructionAt(addr(0x2000));
|
||||||
assertEquals(FlowOverride.NONE, instructionAt.getFlowOverride());
|
assertEquals(FlowOverride.NONE, instructionAt.getFlowOverride());
|
||||||
|
|
||||||
@@ -1031,9 +1026,6 @@ public class CodeManagerTest extends AbstractGenericTest {
|
|||||||
instructionAt = pdb.getListing().getInstructionAt(addr(0x2002));
|
instructionAt = pdb.getListing().getInstructionAt(addr(0x2002));
|
||||||
assertEquals(null, instructionAt);
|
assertEquals(null, instructionAt);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Address addr(long l) {
|
private Address addr(long l) {
|
||||||
|
|||||||
+4
-4
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -79,12 +79,12 @@ public class DefaultDataCacheTest extends AbstractGenericTest {
|
|||||||
assertTrue(cu instanceof Data);
|
assertTrue(cu instanceof Data);
|
||||||
DataDB data = (DataDB) cu;
|
DataDB data = (DataDB) cu;
|
||||||
assertTrue(!data.isDefined());
|
assertTrue(!data.isDefined());
|
||||||
assertTrue(!((Boolean) invokeInstanceMethod("isInvalid", data)));
|
assertFalse((Boolean) invokeInstanceMethod("needsRefreshing", data));
|
||||||
AddressSet restrictedSet = new AddressSet(addr(0x1000), addr(0x1003));
|
AddressSet restrictedSet = new AddressSet(addr(0x1000), addr(0x1003));
|
||||||
Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
Disassembler disassembler = Disassembler.getDisassembler(program, TaskMonitor.DUMMY, null);
|
||||||
AddressSetView disAddrs = disassembler.disassemble(addr(0x1000), restrictedSet);
|
AddressSetView disAddrs = disassembler.disassemble(addr(0x1000), restrictedSet);
|
||||||
assertTrue(!disAddrs.isEmpty());
|
assertTrue(!disAddrs.isEmpty());
|
||||||
assertTrue(!((Boolean) invokeInstanceMethod("checkIsValid", data)));
|
assertFalse((Boolean) invokeInstanceMethod("needsRefreshing", data));
|
||||||
assertNull(listing.getCodeUnitAt(addr(0x1001)));
|
assertNull(listing.getCodeUnitAt(addr(0x1001)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -19,14 +19,13 @@ import static ghidra.feature.fid.db.FunctionsTable.*;
|
|||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.feature.fid.hash.FidHashQuad;
|
import ghidra.feature.fid.hash.FidHashQuad;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
import ghidra.util.NumericUtilities;
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a function record in the FID database.
|
* Represents a function record in the FID database.
|
||||||
*/
|
*/
|
||||||
public class FunctionRecord extends DatabaseObject implements FidHashQuad {
|
public class FunctionRecord extends DbObject implements FidHashQuad {
|
||||||
public final static int HAS_TERMINATOR_FLAG = 1;
|
public final static int HAS_TERMINATOR_FLAG = 1;
|
||||||
public final static int AUTO_PASS_FLAG = 2;
|
public final static int AUTO_PASS_FLAG = 2;
|
||||||
public final static int AUTO_FAIL_FLAG = 4;
|
public final static int AUTO_FAIL_FLAG = 4;
|
||||||
@@ -46,11 +45,10 @@ public class FunctionRecord extends DatabaseObject implements FidHashQuad {
|
|||||||
/**
|
/**
|
||||||
* Package private constructor, to be called from FunctionsTable exclusively.
|
* Package private constructor, to be called from FunctionsTable exclusively.
|
||||||
* @param fid database (for string references)
|
* @param fid database (for string references)
|
||||||
* @param cache FunctionRecord object cache
|
|
||||||
* @param record record for this function
|
* @param record record for this function
|
||||||
*/
|
*/
|
||||||
FunctionRecord(FidDB fid, DBObjectCache<FunctionRecord> cache, DBRecord record) {
|
FunctionRecord(FidDB fid, DBRecord record) {
|
||||||
super(cache, record.getKey());
|
super(record.getKey());
|
||||||
this.record = record;
|
this.record = record;
|
||||||
this.fidDb = fid;
|
this.fidDb = fid;
|
||||||
}
|
}
|
||||||
@@ -169,6 +167,7 @@ public class FunctionRecord extends DatabaseObject implements FidHashQuad {
|
|||||||
byte val = record.getByteValue(FLAGS_COL);
|
byte val = record.getByteValue(FLAGS_COL);
|
||||||
return ((val & FORCE_RELATION_FLAG) != 0);
|
return ((val & FORCE_RELATION_FLAG) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the record id (primary key).
|
* Returns the record id (primary key).
|
||||||
* @return the record id
|
* @return the record id
|
||||||
|
|||||||
+49
-89
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -22,7 +22,9 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import ghidra.feature.fid.hash.FidHashQuad;
|
import ghidra.feature.fid.hash.FidHashQuad;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbFactory;
|
||||||
|
import ghidra.program.database.DbCache;
|
||||||
|
import ghidra.util.Lock;
|
||||||
import ghidra.util.UniversalIdGenerator;
|
import ghidra.util.UniversalIdGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,20 +64,19 @@ public class FunctionsTable {
|
|||||||
Table table;
|
Table table;
|
||||||
FidDB fidDb;
|
FidDB fidDb;
|
||||||
StringsTable stringsTable;
|
StringsTable stringsTable;
|
||||||
DBObjectCache<FunctionRecord> functionCache;
|
DbCache<FunctionRecord> functionCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates or attaches a functions table.
|
* Creates or attaches a functions table.
|
||||||
|
* @param fid the fid database
|
||||||
* @param handle database handle
|
* @param handle database handle
|
||||||
* @param stringsTable strings table (must be created first!)
|
|
||||||
* @param create whether to create or just attach
|
|
||||||
* @throws IOException if create fails
|
* @throws IOException if create fails
|
||||||
*/
|
*/
|
||||||
public FunctionsTable(FidDB fid, DBHandle handle) throws IOException {
|
public FunctionsTable(FidDB fid, DBHandle handle) throws IOException {
|
||||||
table = handle.getTable(FUNCTIONS_TABLE);
|
table = handle.getTable(FUNCTIONS_TABLE);
|
||||||
this.fidDb = fid;
|
this.fidDb = fid;
|
||||||
this.stringsTable = fid.getStringsTable();
|
this.stringsTable = fid.getStringsTable();
|
||||||
functionCache = new DBObjectCache<>(CACHE_SIZE);
|
functionCache = new DbCache<>(new FunctionRecordFactory(), new Lock("Fid"), CACHE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createTable(DBHandle handle) throws IOException {
|
public static void createTable(DBHandle handle) throws IOException {
|
||||||
@@ -116,11 +117,7 @@ public class FunctionsTable {
|
|||||||
if (record.getLongValue(SPECIFIC_HASH_COL) != hash) {
|
if (record.getLongValue(SPECIFIC_HASH_COL) != hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FunctionRecord functionRecord = functionCache.get(record);
|
list.add(functionCache.getCachedInstance(record));
|
||||||
if (functionRecord == null) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
|
||||||
list.add(functionRecord);
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -141,12 +138,10 @@ public class FunctionsTable {
|
|||||||
List<FunctionRecord> list = new ArrayList<>();
|
List<FunctionRecord> list = new ArrayList<>();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Field key = iterator.next();
|
Field key = iterator.next();
|
||||||
FunctionRecord functionRecord = functionCache.get(key.getLongValue());
|
FunctionRecord functionRecord = functionCache.getCachedInstance(key.getLongValue());
|
||||||
if (functionRecord == null) {
|
if (functionRecord != null) {
|
||||||
DBRecord record = table.getRecord(key);
|
list.add(functionRecord);
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
}
|
||||||
list.add(functionRecord);
|
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -179,7 +174,8 @@ public class FunctionsTable {
|
|||||||
byte flags = (byte) (hasTerminator ? FunctionRecord.HAS_TERMINATOR_FLAG : 0);
|
byte flags = (byte) (hasTerminator ? FunctionRecord.HAS_TERMINATOR_FLAG : 0);
|
||||||
record.setByteValue(FLAGS_COL, flags);
|
record.setByteValue(FLAGS_COL, flags);
|
||||||
table.putRecord(record);
|
table.putRecord(record);
|
||||||
FunctionRecord functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
FunctionRecord functionRecord = new FunctionRecord(fidDb, record);
|
||||||
|
functionCache.add(functionRecord);
|
||||||
return functionRecord;
|
return functionRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +184,7 @@ public class FunctionsTable {
|
|||||||
* @param functionID is the id of the function record to modify
|
* @param functionID is the id of the function record to modify
|
||||||
* @param flagMask is the bit to modify
|
* @param flagMask is the bit to modify
|
||||||
* @param value is true to set, false to clear
|
* @param value is true to set, false to clear
|
||||||
* @throws IOException
|
* @throws IOException if error occurs while reading database
|
||||||
*/
|
*/
|
||||||
void modifyFlags(long functionID, int flagMask, boolean value) throws IOException {
|
void modifyFlags(long functionID, int flagMask, boolean value) throws IOException {
|
||||||
DBRecord record = table.getRecord(functionID);
|
DBRecord record = table.getRecord(functionID);
|
||||||
@@ -217,30 +213,12 @@ public class FunctionsTable {
|
|||||||
*/
|
*/
|
||||||
public List<FunctionRecord> getFunctionRecordsByNameSubstring(String nameSearch)
|
public List<FunctionRecord> getFunctionRecordsByNameSubstring(String nameSearch)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
DBFieldIterator iterator = table.indexKeyIterator(NAME_ID_COL);
|
|
||||||
|
|
||||||
if (!iterator.hasNext()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
List<FunctionRecord> list = new ArrayList<>();
|
List<FunctionRecord> list = new ArrayList<>();
|
||||||
|
RecordIterator iterator = table.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Field key = iterator.next();
|
DBRecord record = iterator.next();
|
||||||
FunctionRecord functionRecord = functionCache.get(key.getLongValue());
|
FunctionRecord functionRecord = functionCache.getCachedInstance(record);
|
||||||
if (functionRecord == null) {
|
if (functionRecord.getName().contains(nameSearch)) {
|
||||||
DBRecord record = table.getRecord(key);
|
|
||||||
long nameID = record.getLongValue(NAME_ID_COL);
|
|
||||||
StringRecord nameRecord = stringsTable.lookupString(nameID);
|
|
||||||
String name = nameRecord.getValue();
|
|
||||||
if (name.contains(nameSearch)) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!functionRecord.getName().contains(nameSearch)) {
|
|
||||||
functionRecord = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (functionRecord != null) {
|
|
||||||
list.add(functionRecord);
|
list.add(functionRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,32 +234,14 @@ public class FunctionsTable {
|
|||||||
*/
|
*/
|
||||||
public List<FunctionRecord> getFunctionRecordsByNameRegex(String regex) throws IOException {
|
public List<FunctionRecord> getFunctionRecordsByNameRegex(String regex) throws IOException {
|
||||||
Matcher matcher = Pattern.compile(regex).matcher("");
|
Matcher matcher = Pattern.compile(regex).matcher("");
|
||||||
DBFieldIterator iterator = table.indexKeyIterator(NAME_ID_COL);
|
|
||||||
|
|
||||||
if (!iterator.hasNext()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
List<FunctionRecord> list = new ArrayList<>();
|
List<FunctionRecord> list = new ArrayList<>();
|
||||||
|
RecordIterator iterator = table.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Field key = iterator.next();
|
DBRecord record = iterator.next();
|
||||||
FunctionRecord functionRecord = functionCache.get(key.getLongValue());
|
FunctionRecord functionRecord = functionCache.getCachedInstance(record);
|
||||||
if (functionRecord == null) {
|
matcher.reset(functionRecord.getName());
|
||||||
DBRecord record = table.getRecord(key);
|
if (matcher.matches()) {
|
||||||
long nameID = record.getLongValue(NAME_ID_COL);
|
|
||||||
StringRecord nameRecord = stringsTable.lookupString(nameID);
|
|
||||||
String name = nameRecord.getValue();
|
|
||||||
matcher.reset(name);
|
|
||||||
if (matcher.matches()) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
matcher.reset(functionRecord.getName());
|
|
||||||
if (!matcher.matches()) {
|
|
||||||
functionRecord = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (functionRecord != null) {
|
|
||||||
list.add(functionRecord);
|
list.add(functionRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,14 +255,7 @@ public class FunctionsTable {
|
|||||||
* @throws IOException if database seek encounters an error
|
* @throws IOException if database seek encounters an error
|
||||||
*/
|
*/
|
||||||
public FunctionRecord getFunctionByID(long functionID) throws IOException {
|
public FunctionRecord getFunctionByID(long functionID) throws IOException {
|
||||||
FunctionRecord functionRecord = functionCache.get(functionID);
|
return functionCache.getCachedInstance(functionID);
|
||||||
if (functionRecord == null) {
|
|
||||||
DBRecord record = table.getRecord(functionID);
|
|
||||||
if (record != null) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return functionRecord;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -323,11 +276,7 @@ public class FunctionsTable {
|
|||||||
StringRecord domainPathRecord = stringsTable.lookupString(domainPathID);
|
StringRecord domainPathRecord = stringsTable.lookupString(domainPathID);
|
||||||
String domainPath = domainPathRecord.getValue();
|
String domainPath = domainPathRecord.getValue();
|
||||||
if (domainPath.contains(domainPathSearch)) {
|
if (domainPath.contains(domainPathSearch)) {
|
||||||
FunctionRecord functionRecord = functionCache.get(record);
|
list.add(functionCache.getCachedInstance(record));
|
||||||
if (functionRecord == null) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
}
|
|
||||||
list.add(functionRecord);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
@@ -356,20 +305,31 @@ public class FunctionsTable {
|
|||||||
List<FunctionRecord> list = new ArrayList<>();
|
List<FunctionRecord> list = new ArrayList<>();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Field key = iterator.next();
|
Field key = iterator.next();
|
||||||
FunctionRecord functionRecord = functionCache.get(key.getLongValue());
|
FunctionRecord functionRecord = functionCache.getCachedInstance(key.getLongValue());
|
||||||
if (functionRecord == null) {
|
if (functionRecord.getLibraryID() == libraryKey) {
|
||||||
DBRecord record = table.getRecord(key);
|
list.add(functionRecord);
|
||||||
if (record.getLongValue(LIBRARY_ID_COL) == libraryKey) {
|
|
||||||
functionRecord = new FunctionRecord(fidDb, functionCache, record);
|
|
||||||
list.add(functionRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (functionRecord.getLibraryID() == libraryKey) {
|
|
||||||
list.add(functionRecord);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FunctionRecordFactory implements DbFactory<FunctionRecord> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionRecord instantiate(long key) {
|
||||||
|
try {
|
||||||
|
DBRecord record = table.getRecord(key);
|
||||||
|
return record == null ? null : instantiate(record);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException("serious delayed database access error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FunctionRecord instantiate(DBRecord record) {
|
||||||
|
return new FunctionRecord(fidDb, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -15,13 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.feature.fid.db;
|
package ghidra.feature.fid.db;
|
||||||
|
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string record in the FID database.
|
* A string record in the FID database.
|
||||||
*/
|
*/
|
||||||
public class StringRecord extends DatabaseObject {
|
public class StringRecord extends DbObject {
|
||||||
/**
|
/**
|
||||||
* The value of the string.
|
* The value of the string.
|
||||||
*/
|
*/
|
||||||
@@ -29,12 +28,11 @@ public class StringRecord extends DatabaseObject {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with the primary key and the string value.
|
* Constructor with the primary key and the string value.
|
||||||
* @param cache StringRecord object cache
|
|
||||||
* @param key primary key
|
* @param key primary key
|
||||||
* @param value the string value
|
* @param value the string value
|
||||||
*/
|
*/
|
||||||
public StringRecord(DBObjectCache<StringRecord> cache, long key, String value) {
|
public StringRecord(long key, String value) {
|
||||||
super(cache, key);
|
super(key);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -18,7 +18,9 @@ package ghidra.feature.fid.db;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbFactory;
|
||||||
|
import ghidra.program.database.DbCache;
|
||||||
|
import ghidra.util.Lock;
|
||||||
import ghidra.util.UniversalIdGenerator;
|
import ghidra.util.UniversalIdGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,17 +45,16 @@ public class StringsTable {
|
|||||||
static int[] INDEXED_COLUMNS = new int[] { STRING_VALUE_COL };
|
static int[] INDEXED_COLUMNS = new int[] { STRING_VALUE_COL };
|
||||||
|
|
||||||
Table table;
|
Table table;
|
||||||
DBObjectCache<StringRecord> stringCache;
|
DbCache<StringRecord> stringCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates or attaches the string table.
|
* Creates or attaches the string table.
|
||||||
* @param handle the database handle
|
* @param handle the database handle
|
||||||
* @param create whether to create or attach
|
|
||||||
* @throws IOException if the database has a problem
|
* @throws IOException if the database has a problem
|
||||||
*/
|
*/
|
||||||
public StringsTable(DBHandle handle) throws IOException {
|
public StringsTable(DBHandle handle) throws IOException {
|
||||||
table = handle.getTable(STRINGS_TABLE);
|
table = handle.getTable(STRINGS_TABLE);
|
||||||
stringCache = new DBObjectCache<>(CACHE_SIZE);
|
stringCache = new DbCache<>(new StringRecordFactory(), new Lock("Fid"), CACHE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createTable(DBHandle handle) throws IOException {
|
public static void createTable(DBHandle handle) throws IOException {
|
||||||
@@ -99,20 +100,25 @@ public class StringsTable {
|
|||||||
* @return the string record, or null if no such key
|
* @return the string record, or null if no such key
|
||||||
*/
|
*/
|
||||||
StringRecord lookupString(long stringID) {
|
StringRecord lookupString(long stringID) {
|
||||||
StringRecord stringRecord = stringCache.get(stringID);
|
return stringCache.getCachedInstance(stringID);
|
||||||
if (stringRecord == null) {
|
}
|
||||||
DBRecord record;
|
|
||||||
|
class StringRecordFactory implements DbFactory<StringRecord> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StringRecord instantiate(long key) {
|
||||||
try {
|
try {
|
||||||
record = table.getRecord(stringID);
|
DBRecord record = table.getRecord(key);
|
||||||
if (record != null) {
|
return record == null ? null : instantiate(record);
|
||||||
stringRecord =
|
|
||||||
new StringRecord(stringCache, stringID, record.getString(STRING_VALUE_COL));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new RuntimeException("serious delayed database access error", e);
|
throw new RuntimeException("serious delayed database access error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return stringRecord;
|
|
||||||
|
@Override
|
||||||
|
public StringRecord instantiate(DBRecord record) {
|
||||||
|
return new StringRecord(record.getKey(), record.getString(STRING_VALUE_COL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-133
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -25,9 +25,11 @@ import ghidra.feature.vt.api.impl.*;
|
|||||||
import ghidra.feature.vt.api.main.*;
|
import ghidra.feature.vt.api.main.*;
|
||||||
import ghidra.feature.vt.api.util.VTAssociationStatusException;
|
import ghidra.feature.vt.api.util.VTAssociationStatusException;
|
||||||
import ghidra.framework.data.OpenMode;
|
import ghidra.framework.data.OpenMode;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbFactory;
|
||||||
|
import ghidra.program.database.DbCache;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -39,9 +41,9 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
private VTAssociationTableDBAdapter associationTableAdapter;
|
private VTAssociationTableDBAdapter associationTableAdapter;
|
||||||
|
|
||||||
private VTMatchMarkupItemTableDBAdapter markupItemTableAdapter;
|
private VTMatchMarkupItemTableDBAdapter markupItemTableAdapter;
|
||||||
private DBObjectCache<MarkupItemStorageDB> markupItemCache;
|
private DbCache<MarkupItemStorageDB> markupItemCache;
|
||||||
private List<AssociationHook> associationHooks = new ArrayList<>();
|
private List<AssociationHook> associationHooks = new ArrayList<>();
|
||||||
private DBObjectCache<VTAssociationDB> associationCache;
|
private DbCache<VTAssociationDB> associationCache;
|
||||||
private AcceptedStatusCache acceptedStatusCache = new AcceptedStatusCache();
|
private AcceptedStatusCache acceptedStatusCache = new AcceptedStatusCache();
|
||||||
Lock lock;
|
Lock lock;
|
||||||
|
|
||||||
@@ -67,8 +69,8 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
AssociationDatabaseManager(VTSessionDB session) {
|
AssociationDatabaseManager(VTSessionDB session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
lock = session.getLock();
|
lock = session.getLock();
|
||||||
associationCache = new DBObjectCache<>(10);
|
associationCache = new DbCache<>(new AssociationFactory(), lock, 10);
|
||||||
markupItemCache = new DBObjectCache<>(10);
|
markupItemCache = new DbCache<>(new MarkupFactory(), lock, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,8 +86,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
|
|
||||||
Collection<MarkupItemStorageDB> items = new ArrayList<>();
|
Collection<MarkupItemStorageDB> items = new ArrayList<>();
|
||||||
VTAssociationDB associationDB = (VTAssociationDB) association;
|
VTAssociationDB associationDB = (VTAssociationDB) association;
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
int recordCount = markupItemTableAdapter.getRecordCount();
|
int recordCount = markupItemTableAdapter.getRecordCount();
|
||||||
if (recordCount == 0) {
|
if (recordCount == 0) {
|
||||||
recordCount = 1; // to give the appearance of progress
|
recordCount = 1; // to give the appearance of progress
|
||||||
@@ -98,7 +99,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
while (records.hasNext()) {
|
while (records.hasNext()) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
DBRecord record = records.next();
|
DBRecord record = records.next();
|
||||||
items.add(getMarkupItemForRecord(record));
|
items.add(markupItemCache.getCachedInstance(record));
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,9 +108,6 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,20 +146,6 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MarkupItemStorageDB getMarkupItemForRecord(DBRecord markupItemRecord) {
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
MarkupItemStorageDB markupItem = markupItemCache.get(markupItemRecord);
|
|
||||||
if (markupItem == null) {
|
|
||||||
markupItem = new MarkupItemStorageDB(markupItemRecord, markupItemCache, this);
|
|
||||||
}
|
|
||||||
return markupItem;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Address getDestinationAddressFromLong(long longValue) {
|
Address getDestinationAddressFromLong(long longValue) {
|
||||||
return session.getDestinationAddressFromLong(longValue);
|
return session.getDestinationAddressFromLong(longValue);
|
||||||
}
|
}
|
||||||
@@ -188,7 +172,8 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DBRecord record = markupItemTableAdapter.createMarkupItemRecord(markupItem);
|
DBRecord record = markupItemTableAdapter.createMarkupItemRecord(markupItem);
|
||||||
MarkupItemStorageDB appliedMarkupItem = getMarkupItemForRecord(record);
|
MarkupItemStorageDB appliedMarkupItem = new MarkupItemStorageDB(record, this);
|
||||||
|
markupItemCache.add(appliedMarkupItem);
|
||||||
return appliedMarkupItem;
|
return appliedMarkupItem;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
@@ -213,18 +198,15 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
boolean isBlocked = isBlocked(sourceAddress, destinationAddress);
|
boolean isBlocked = isBlocked(sourceAddress, destinationAddress);
|
||||||
|
|
||||||
VTAssociationDB newAssociation = null;
|
VTAssociationDB newAssociation = null;
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
DBRecord record = associationTableAdapter.insertRecord(sourceLong, destinationLong,
|
DBRecord record = associationTableAdapter.insertRecord(sourceLong, destinationLong,
|
||||||
type, isBlocked ? BLOCKED : AVAILABLE, 0);
|
type, isBlocked ? BLOCKED : AVAILABLE, 0);
|
||||||
newAssociation = new VTAssociationDB(this, associationCache, record);
|
newAssociation = new VTAssociationDB(this, record);
|
||||||
|
associationCache.add(newAssociation);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
session.setChanged(VTEvent.ASSOCIATION_ADDED, null, newAssociation);
|
session.setChanged(VTEvent.ASSOCIATION_ADDED, null, newAssociation);
|
||||||
return newAssociation;
|
return newAssociation;
|
||||||
}
|
}
|
||||||
@@ -274,33 +256,28 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
@Override
|
@Override
|
||||||
public List<VTAssociation> getAssociations() {
|
public List<VTAssociation> getAssociations() {
|
||||||
List<VTAssociation> list = new ArrayList<>();
|
List<VTAssociation> list = new ArrayList<>();
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
RecordIterator iterator = associationTableAdapter.getRecords();
|
RecordIterator iterator = associationTableAdapter.getRecords();
|
||||||
for (; iterator.hasNext();) {
|
for (; iterator.hasNext();) {
|
||||||
DBRecord nextRecord = iterator.next();
|
DBRecord nextRecord = iterator.next();
|
||||||
list.add(getAssociationForRecord(nextRecord));
|
list.add(associationCache.getCachedInstance(nextRecord));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTAssociation getAssociation(Address sourceAddress, Address destinationAddress) {
|
public VTAssociation getAssociation(Address sourceAddress, Address destinationAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
long addressKey = session.getLongFromSourceAddress(sourceAddress);
|
long addressKey = session.getLongFromSourceAddress(sourceAddress);
|
||||||
RecordIterator iterator =
|
RecordIterator iterator =
|
||||||
associationTableAdapter.getRecordsForSourceAddress(addressKey);
|
associationTableAdapter.getRecordsForSourceAddress(addressKey);
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
DBRecord record = iterator.next();
|
DBRecord record = iterator.next();
|
||||||
VTAssociationDB associationDB = getAssociationForRecord(record);
|
VTAssociationDB associationDB = associationCache.getCachedInstance(record);
|
||||||
if (associationDB.getDestinationAddress().equals(destinationAddress)) {
|
if (associationDB.getDestinationAddress().equals(destinationAddress)) {
|
||||||
return associationDB;
|
return associationDB;
|
||||||
}
|
}
|
||||||
@@ -309,9 +286,6 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +297,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
associationTableAdapter.getRecordsForSourceAddress(addressKey);
|
associationTableAdapter.getRecordsForSourceAddress(addressKey);
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
DBRecord record = iterator.next();
|
DBRecord record = iterator.next();
|
||||||
VTAssociationDB associationDB = getAssociationForRecord(record);
|
VTAssociationDB associationDB = associationCache.getCachedInstance(record);
|
||||||
Address dbDestinatonAddress = associationDB.getDestinationAddress();
|
Address dbDestinatonAddress = associationDB.getDestinationAddress();
|
||||||
if (destinationAddress.equals(dbDestinatonAddress)) {
|
if (destinationAddress.equals(dbDestinatonAddress)) {
|
||||||
return associationDB;
|
return associationDB;
|
||||||
@@ -337,99 +311,51 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTAssociationDB getAssociationForRecord(DBRecord record) {
|
|
||||||
if (record == null) {
|
|
||||||
throw new AssertException("How can we have a null record?!!!");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
VTAssociationDB associationDB = associationCache.get(record);
|
|
||||||
if (associationDB == null) {
|
|
||||||
associationDB = new VTAssociationDB(this, associationCache, record);
|
|
||||||
}
|
|
||||||
return associationDB;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VTAssociationDB getAssociation(long associationKey) {
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
VTAssociationDB associationDB = associationCache.get(associationKey);
|
|
||||||
if (associationDB != null) {
|
|
||||||
return associationDB;
|
|
||||||
}
|
|
||||||
DBRecord record = associationTableAdapter.getRecord(associationKey);
|
|
||||||
if (record == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new VTAssociationDB(this, associationCache, record);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
session.dbError(e);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VTSessionDB getSession() {
|
public VTSessionDB getSession() {
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VTAssociation> getRelatedAssociationsBySourceAddress(Address sourceAddress) {
|
public Collection<VTAssociation> getRelatedAssociationsBySourceAddress(Address sourceAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
long sourceId = session.getLongFromSourceAddress(sourceAddress);
|
long sourceId = session.getLongFromSourceAddress(sourceAddress);
|
||||||
Set<DBRecord> relatedRecords =
|
Set<DBRecord> relatedRecords =
|
||||||
associationTableAdapter.getRelatedAssociationRecordsBySourceAddress(sourceId);
|
associationTableAdapter.getRelatedAssociationRecordsBySourceAddress(sourceId);
|
||||||
List<VTAssociation> associations = new ArrayList<>();
|
List<VTAssociation> associations = new ArrayList<>();
|
||||||
for (DBRecord record : relatedRecords) {
|
for (DBRecord record : relatedRecords) {
|
||||||
associations.add(getAssociationForRecord(record));
|
associations.add(associationCache.getCachedInstance(record));
|
||||||
}
|
}
|
||||||
return associations;
|
return associations;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VTAssociation> getRelatedAssociationsByDestinationAddress(
|
public Collection<VTAssociation> getRelatedAssociationsByDestinationAddress(
|
||||||
Address destinationAddress) {
|
Address destinationAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
long destinationId = session.getLongFromDestinationAddress(destinationAddress);
|
long destinationId = session.getLongFromDestinationAddress(destinationAddress);
|
||||||
Set<DBRecord> relatedRecords = associationTableAdapter
|
Set<DBRecord> relatedRecords = associationTableAdapter
|
||||||
.getRelatedAssociationRecordsByDestinationAddress(destinationId);
|
.getRelatedAssociationRecordsByDestinationAddress(destinationId);
|
||||||
List<VTAssociation> associations = new ArrayList<>();
|
List<VTAssociation> associations = new ArrayList<>();
|
||||||
for (DBRecord record : relatedRecords) {
|
for (DBRecord record : relatedRecords) {
|
||||||
associations.add(getAssociationForRecord(record));
|
associations.add(associationCache.getCachedInstance(record));
|
||||||
}
|
}
|
||||||
return associations;
|
return associations;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VTAssociation> getRelatedAssociationsBySourceAndDestinationAddress(
|
public Collection<VTAssociation> getRelatedAssociationsBySourceAndDestinationAddress(
|
||||||
Address sourceAddress, Address destinationAddress) {
|
Address sourceAddress, Address destinationAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
long sourceId = session.getLongFromSourceAddress(sourceAddress);
|
long sourceId = session.getLongFromSourceAddress(sourceAddress);
|
||||||
long destinationId = session.getLongFromDestinationAddress(destinationAddress);
|
long destinationId = session.getLongFromDestinationAddress(destinationAddress);
|
||||||
Set<DBRecord> relatedRecords =
|
Set<DBRecord> relatedRecords =
|
||||||
@@ -437,16 +363,13 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
sourceId, destinationId);
|
sourceId, destinationId);
|
||||||
List<VTAssociation> associations = new ArrayList<>();
|
List<VTAssociation> associations = new ArrayList<>();
|
||||||
for (DBRecord record : relatedRecords) {
|
for (DBRecord record : relatedRecords) {
|
||||||
associations.add(getAssociationForRecord(record));
|
associations.add(associationCache.getCachedInstance(record));
|
||||||
}
|
}
|
||||||
return associations;
|
return associations;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,7 +487,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
sourceId, destinationId);
|
sourceId, destinationId);
|
||||||
relatedRecords.remove(association.getRecord()); // don't change the given association
|
relatedRecords.remove(association.getRecord()); // don't change the given association
|
||||||
for (DBRecord record : relatedRecords) {
|
for (DBRecord record : relatedRecords) {
|
||||||
relatedAssociaitons.add(getAssociationForRecord(record));
|
relatedAssociaitons.add(associationCache.getCachedInstance(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
@@ -581,7 +504,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
VTAssociationDB association = getAssociationForRecord(record);
|
VTAssociationDB association = associationCache.getCachedInstance(record);
|
||||||
Address sourceAddress = association.getSourceAddress();
|
Address sourceAddress = association.getSourceAddress();
|
||||||
Address destinationAddress = association.getDestinationAddress();
|
Address destinationAddress = association.getDestinationAddress();
|
||||||
VTAssociationStatus status = association.getStatus();
|
VTAssociationStatus status = association.getStatus();
|
||||||
@@ -619,6 +542,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
void removeMarkupRecord(long key) {
|
void removeMarkupRecord(long key) {
|
||||||
try {
|
try {
|
||||||
markupItemTableAdapter.removeMarkupItemRecord(key);
|
markupItemTableAdapter.removeMarkupItemRecord(key);
|
||||||
|
markupItemCache.delete(key);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
session.dbError(e);
|
session.dbError(e);
|
||||||
@@ -661,60 +585,43 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
private boolean disposed = false;
|
private boolean disposed = false;
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
disposed = true;
|
disposed = true;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate() {
|
void invalidate() {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
invalid = true;
|
invalid = true;
|
||||||
acceptedSourceAssociations.clear();
|
acceptedSourceAssociations.clear();
|
||||||
acceptedDestinationAssociations.clear();
|
acceptedDestinationAssociations.clear();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(Address sourceAddress, Address destinationAddress) {
|
void remove(Address sourceAddress, Address destinationAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
if (disposed || invalid) {
|
if (disposed || invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceptedSourceAssociations.remove(sourceAddress);
|
acceptedSourceAssociations.remove(sourceAddress);
|
||||||
acceptedDestinationAssociations.remove(destinationAddress);
|
acceptedDestinationAssociations.remove(destinationAddress);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(Address sourceAddress, Address destinationAddress) {
|
void add(Address sourceAddress, Address destinationAddress) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
if (disposed || invalid) {
|
if (disposed || invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceptedSourceAssociations.add(sourceAddress);
|
acceptedSourceAssociations.add(sourceAddress);
|
||||||
acceptedDestinationAssociations.add(destinationAddress);
|
acceptedDestinationAssociations.add(destinationAddress);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isBlocked(Address sourceAddress, Address destinationAddress) {
|
boolean isBlocked(Address sourceAddress, Address destinationAddress) {
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
|
|
||||||
if (disposed) {
|
if (disposed) {
|
||||||
return true;
|
return true;
|
||||||
@@ -735,9 +642,6 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -749,7 +653,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
.getRelatedAssociationRecordsBySourceAndDestinationAddress(sourceID,
|
.getRelatedAssociationRecordsBySourceAndDestinationAddress(sourceID,
|
||||||
destinationID);
|
destinationID);
|
||||||
for (DBRecord record : relatedRecords) {
|
for (DBRecord record : relatedRecords) {
|
||||||
VTAssociationDB associationDB = getAssociationForRecord(record);
|
VTAssociationDB associationDB = associationCache.getCachedInstance(record);
|
||||||
VTAssociationStatus status = associationDB.getStatus();
|
VTAssociationStatus status = associationDB.getStatus();
|
||||||
if (status == ACCEPTED) {
|
if (status == ACCEPTED) {
|
||||||
return true;
|
return true;
|
||||||
@@ -785,7 +689,7 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
monitor.increment();
|
monitor.increment();
|
||||||
DBRecord record = it.next();
|
DBRecord record = it.next();
|
||||||
VTAssociationDB associationDB = getAssociationForRecord(record);
|
VTAssociationDB associationDB = associationCache.getCachedInstance(record);
|
||||||
VTAssociationStatus status = associationDB.getStatus();
|
VTAssociationStatus status = associationDB.getStatus();
|
||||||
if (status == ACCEPTED) {
|
if (status == ACCEPTED) {
|
||||||
Address sourceAddress = associationDB.getSourceAddress();
|
Address sourceAddress = associationDB.getSourceAddress();
|
||||||
@@ -804,4 +708,48 @@ public class AssociationDatabaseManager implements VTAssociationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class AssociationFactory implements DbFactory<VTAssociationDB> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VTAssociationDB instantiate(long key) {
|
||||||
|
try {
|
||||||
|
DBRecord record = associationTableAdapter.getRecord(key);
|
||||||
|
return record == null ? null : instantiate(record);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
session.dbError(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VTAssociationDB instantiate(DBRecord rec) {
|
||||||
|
return new VTAssociationDB(AssociationDatabaseManager.this, rec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MarkupFactory implements DbFactory<MarkupItemStorageDB> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MarkupItemStorageDB instantiate(long key) {
|
||||||
|
try {
|
||||||
|
DBRecord record = markupItemTableAdapter.getRecord(key);
|
||||||
|
return record == null ? null : instantiate(record);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
session.dbError(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MarkupItemStorageDB instantiate(DBRecord rec) {
|
||||||
|
return new MarkupItemStorageDB(rec, AssociationDatabaseManager.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VTAssociation getAssociation(long associationKey) {
|
||||||
|
return associationCache.getCachedInstance(associationKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-30
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -25,22 +25,21 @@ import ghidra.feature.vt.api.main.VTMarkupItemStatus;
|
|||||||
import ghidra.feature.vt.api.markuptype.VTMarkupType;
|
import ghidra.feature.vt.api.markuptype.VTMarkupType;
|
||||||
import ghidra.feature.vt.api.markuptype.VTMarkupTypeFactory;
|
import ghidra.feature.vt.api.markuptype.VTMarkupTypeFactory;
|
||||||
import ghidra.feature.vt.api.util.Stringable;
|
import ghidra.feature.vt.api.util.Stringable;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
|
|
||||||
public class MarkupItemStorageDB extends DatabaseObject implements MarkupItemStorage {
|
public class MarkupItemStorageDB extends DbObject implements MarkupItemStorage {
|
||||||
private final AssociationDatabaseManager associationManager;
|
private final AssociationDatabaseManager associationManager;
|
||||||
private final VTAssociation association;
|
private final VTAssociation association;
|
||||||
private final VTSessionDB session;
|
private final VTSessionDB session;
|
||||||
|
|
||||||
private DBRecord record;
|
private DBRecord record;
|
||||||
|
|
||||||
MarkupItemStorageDB(DBRecord record, DBObjectCache<MarkupItemStorageDB> cache,
|
MarkupItemStorageDB(DBRecord record, AssociationDatabaseManager associationManager) {
|
||||||
AssociationDatabaseManager associationManager) {
|
super(record.getKey());
|
||||||
super(cache, record.getKey());
|
|
||||||
this.record = record;
|
this.record = record;
|
||||||
this.associationManager = associationManager;
|
this.associationManager = associationManager;
|
||||||
this.session = associationManager.getSession();
|
this.session = associationManager.getSession();
|
||||||
@@ -60,47 +59,67 @@ public class MarkupItemStorageDB extends DatabaseObject implements MarkupItemSto
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getSourceAddress() {
|
public Address getSourceAddress() {
|
||||||
long addressLong = record.getLongValue(SOURCE_ADDRESS_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
Program program = session.getSourceProgram();
|
refreshIfNeeded();
|
||||||
AddressMap addressMap = program.getAddressMap();
|
long addressLong = record.getLongValue(SOURCE_ADDRESS_COL.column());
|
||||||
return addressMap.decodeAddress(addressLong);
|
Program program = session.getSourceProgram();
|
||||||
|
AddressMap addressMap = program.getAddressMap();
|
||||||
|
return addressMap.decodeAddress(addressLong);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getDestinationAddress() {
|
public Address getDestinationAddress() {
|
||||||
long addressLong = record.getLongValue(DESTINATION_ADDRESS_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
Program program = session.getDestinationProgram();
|
refreshIfNeeded();
|
||||||
AddressMap addressMap = program.getAddressMap();
|
long addressLong = record.getLongValue(DESTINATION_ADDRESS_COL.column());
|
||||||
return addressMap.decodeAddress(addressLong);
|
Program program = session.getDestinationProgram();
|
||||||
|
AddressMap addressMap = program.getAddressMap();
|
||||||
|
return addressMap.decodeAddress(addressLong);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDestinationAddressSource() {
|
public String getDestinationAddressSource() {
|
||||||
return record.getString(ADDRESS_SOURCE_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
|
refreshIfNeeded();
|
||||||
|
return record.getString(ADDRESS_SOURCE_COL.column());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTMarkupItemStatus getStatus() {
|
public VTMarkupItemStatus getStatus() {
|
||||||
checkIsValid();
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
byte ordinal = record.getByteValue(STATUS_COL.column());
|
refreshIfNeeded();
|
||||||
return VTMarkupItemStatus.values()[ordinal];
|
byte ordinal = record.getByteValue(STATUS_COL.column());
|
||||||
|
return VTMarkupItemStatus.values()[ordinal];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStatusDescription() {
|
public String getStatusDescription() {
|
||||||
return record.getString(STATUS_DESCRIPTION_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
|
refreshIfNeeded();
|
||||||
|
return record.getString(STATUS_DESCRIPTION_COL.column());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stringable getSourceValue() {
|
public Stringable getSourceValue() {
|
||||||
String string = record.getString(SOURCE_VALUE_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
return Stringable.getStringable(string, session.getSourceProgram());
|
refreshIfNeeded();
|
||||||
|
String string = record.getString(SOURCE_VALUE_COL.column());
|
||||||
|
return Stringable.getStringable(string, session.getSourceProgram());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stringable getDestinationValue() {
|
public Stringable getDestinationValue() {
|
||||||
String string = record.getString(ORIGINAL_DESTINATION_VALUE_COL.column());
|
try (Closeable c = associationManager.lock.read()) {
|
||||||
return Stringable.getStringable(string, session.getDestinationProgram());
|
refreshIfNeeded();
|
||||||
|
String string = record.getString(ORIGINAL_DESTINATION_VALUE_COL.column());
|
||||||
|
return Stringable.getStringable(string, session.getDestinationProgram());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,16 +150,13 @@ public class MarkupItemStorageDB extends DatabaseObject implements MarkupItemSto
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MarkupItemStorage reset() {
|
public MarkupItemStorage reset() {
|
||||||
associationManager.lock.acquire();
|
try (Closeable c = associationManager.lock.write()) {
|
||||||
try {
|
checkDeleted();
|
||||||
MarkupItemStorage storage = new MarkupItemStorageImpl(getAssociation(), getMarkupType(),
|
MarkupItemStorage storage = new MarkupItemStorageImpl(getAssociation(), getMarkupType(),
|
||||||
getSourceAddress(), getDestinationAddress(), getDestinationAddressSource());
|
getSourceAddress(), getDestinationAddress(), getDestinationAddressSource());
|
||||||
associationManager.removeMarkupRecord(record.getKey());
|
associationManager.removeMarkupRecord(record.getKey());
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationManager.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+29
-65
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -24,21 +24,25 @@ import ghidra.feature.vt.api.impl.MarkupItemManagerImpl;
|
|||||||
import ghidra.feature.vt.api.impl.VTEvent;
|
import ghidra.feature.vt.api.impl.VTEvent;
|
||||||
import ghidra.feature.vt.api.main.*;
|
import ghidra.feature.vt.api.main.*;
|
||||||
import ghidra.feature.vt.api.util.VTAssociationStatusException;
|
import ghidra.feature.vt.api.util.VTAssociationStatusException;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
public class VTAssociationDB extends DbObject implements VTAssociation {
|
||||||
|
|
||||||
public DBRecord record;
|
public DBRecord record;
|
||||||
private MarkupItemManagerImpl markupManager;
|
private MarkupItemManagerImpl markupManager;
|
||||||
public final AssociationDatabaseManager associationDBM;
|
public final AssociationDatabaseManager associationDBM;
|
||||||
|
|
||||||
public VTAssociationDB(AssociationDatabaseManager associationManager,
|
/**
|
||||||
DBObjectCache<VTAssociationDB> cache, DBRecord record) {
|
* Constructor
|
||||||
super(cache, record.getKey());
|
* @param associationManager the association database manager
|
||||||
|
* @param record the record for the association
|
||||||
|
*/
|
||||||
|
VTAssociationDB(AssociationDatabaseManager associationManager, DBRecord record) {
|
||||||
|
super(record.getKey());
|
||||||
this.associationDBM = associationManager;
|
this.associationDBM = associationManager;
|
||||||
this.record = record;
|
this.record = record;
|
||||||
markupManager = new MarkupItemManagerImpl(this);
|
markupManager = new MarkupItemManagerImpl(this);
|
||||||
@@ -93,41 +97,29 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getSourceAddress() {
|
public Address getSourceAddress() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return associationDBM
|
return associationDBM
|
||||||
.getSourceAddressFromLong(record.getLongValue(SOURCE_ADDRESS_COL.column()));
|
.getSourceAddressFromLong(record.getLongValue(SOURCE_ADDRESS_COL.column()));
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Address getDestinationAddress() {
|
public Address getDestinationAddress() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return associationDBM.getDestinationAddressFromLong(
|
return associationDBM.getDestinationAddressFromLong(
|
||||||
record.getLongValue(DESTINATION_ADDRESS_COL.column()));
|
record.getLongValue(DESTINATION_ADDRESS_COL.column()));
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTAssociationType getType() {
|
public VTAssociationType getType() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
byte associationTypeOrdinal = record.getByteValue(TYPE_COL.column());
|
byte associationTypeOrdinal = record.getByteValue(TYPE_COL.column());
|
||||||
return VTAssociationType.values()[associationTypeOrdinal];
|
return VTAssociationType.values()[associationTypeOrdinal];
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markupItemStatusChanged(VTMarkupItem markupItem) {
|
public void markupItemStatusChanged(VTMarkupItem markupItem) {
|
||||||
@@ -136,37 +128,25 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTAssociationStatus getStatus() {
|
public VTAssociationStatus getStatus() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return VTAssociationStatus.values()[record.getByteValue(STATUS_COL.column())];
|
return VTAssociationStatus.values()[record.getByteValue(STATUS_COL.column())];
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTAssociationMarkupStatus getMarkupStatus() {
|
public VTAssociationMarkupStatus getMarkupStatus() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return new VTAssociationMarkupStatus(record.getByteValue(APPLIED_STATUS_COL.column()));
|
return new VTAssociationMarkupStatus(record.getByteValue(APPLIED_STATUS_COL.column()));
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DBRecord getRecord() {
|
DBRecord getRecord() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -220,20 +200,15 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getVoteCount() {
|
public int getVoteCount() {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
return record.getIntValue(VOTE_COUNT_COL.column());
|
return record.getIntValue(VOTE_COUNT_COL.column());
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMarkupStatus(VTAssociationMarkupStatus status) {
|
public void setMarkupStatus(VTAssociationMarkupStatus status) {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.write()) {
|
||||||
try {
|
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
VTAssociationMarkupStatus existingStatus = getMarkupStatus();
|
VTAssociationMarkupStatus existingStatus = getMarkupStatus();
|
||||||
if (status.equals(existingStatus)) {
|
if (status.equals(existingStatus)) {
|
||||||
@@ -246,14 +221,10 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
.setObjectChanged(VTEvent.ASSOCIATION_MARKUP_STATUS_CHANGED, this,
|
.setObjectChanged(VTEvent.ASSOCIATION_MARKUP_STATUS_CHANGED, this,
|
||||||
existingStatus, status);
|
existingStatus, status);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStatus(VTAssociationStatus status) {
|
public void setStatus(VTAssociationStatus status) {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.write()) {
|
||||||
try {
|
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
VTAssociationStatus existingStatus = getStatus();
|
VTAssociationStatus existingStatus = getStatus();
|
||||||
if (status == existingStatus) {
|
if (status == existingStatus) {
|
||||||
@@ -266,15 +237,11 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
.setObjectChanged(VTEvent.ASSOCIATION_STATUS_CHANGED, this, existingStatus,
|
.setObjectChanged(VTEvent.ASSOCIATION_STATUS_CHANGED, this, existingStatus,
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVoteCount(int voteCount) {
|
public void setVoteCount(int voteCount) {
|
||||||
associationDBM.lock.acquire();
|
try (Closeable c = associationDBM.lock.write()) {
|
||||||
try {
|
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
voteCount = Math.max(0, voteCount);
|
voteCount = Math.max(0, voteCount);
|
||||||
record.setIntValue(VOTE_COUNT_COL.column(), voteCount);
|
record.setIntValue(VOTE_COUNT_COL.column(), voteCount);
|
||||||
@@ -282,9 +249,6 @@ public class VTAssociationDB extends DatabaseObject implements VTAssociation {
|
|||||||
associationDBM.getSession()
|
associationDBM.getSession()
|
||||||
.setObjectChanged(VTEvent.VOTE_COUNT_CHANGED, this, null, null);
|
.setObjectChanged(VTEvent.VOTE_COUNT_CHANGED, this, null, null);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
associationDBM.lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+10
-18
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -23,14 +23,14 @@ import db.DBRecord;
|
|||||||
import ghidra.feature.vt.api.impl.VTEvent;
|
import ghidra.feature.vt.api.impl.VTEvent;
|
||||||
import ghidra.feature.vt.api.impl.VTProgramCorrelatorInfo;
|
import ghidra.feature.vt.api.impl.VTProgramCorrelatorInfo;
|
||||||
import ghidra.feature.vt.api.main.*;
|
import ghidra.feature.vt.api.main.*;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
public class VTMatchDB extends DatabaseObject implements VTMatch {
|
public class VTMatchDB extends DbObject implements VTMatch {
|
||||||
|
|
||||||
private DBRecord record;
|
private DBRecord record;
|
||||||
private final VTMatchSetDB matchSet;
|
private final VTMatchSetDB matchSet;
|
||||||
@@ -42,8 +42,8 @@ public class VTMatchDB extends DatabaseObject implements VTMatch {
|
|||||||
private boolean doCalculateHash = true;
|
private boolean doCalculateHash = true;
|
||||||
private int hash;
|
private int hash;
|
||||||
|
|
||||||
public VTMatchDB(DBObjectCache<VTMatchDB> cache, DBRecord record, VTMatchSetDB matchSet) {
|
public VTMatchDB(DBRecord record, VTMatchSetDB matchSet) {
|
||||||
super(cache, record.getKey());
|
super(record.getKey());
|
||||||
this.record = record;
|
this.record = record;
|
||||||
this.matchSet = matchSet;
|
this.matchSet = matchSet;
|
||||||
session = (VTSessionDB) matchSet.getSession();
|
session = (VTSessionDB) matchSet.getSession();
|
||||||
@@ -105,8 +105,7 @@ public class VTMatchDB extends DatabaseObject implements VTMatch {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTag(VTMatchTag tag) {
|
public void setTag(VTMatchTag tag) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return;
|
return;
|
||||||
@@ -127,9 +126,6 @@ public class VTMatchDB extends DatabaseObject implements VTMatch {
|
|||||||
updateRecord();
|
updateRecord();
|
||||||
session.setObjectChanged(VTEvent.MATCH_TAG_CHANGED, this, oldTag, newTagDB);
|
session.setObjectChanged(VTEvent.MATCH_TAG_CHANGED, this, oldTag, newTagDB);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTAssociation loadAssociation() {
|
private VTAssociation loadAssociation() {
|
||||||
@@ -145,16 +141,12 @@ public class VTMatchDB extends DatabaseObject implements VTMatch {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTAssociation getAssociation() {
|
public VTAssociation getAssociation() {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
refreshIfNeeded();
|
||||||
checkIsValid();
|
|
||||||
if (association == null) {
|
if (association == null) {
|
||||||
association = loadAssociation();
|
association = loadAssociation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return association;
|
return association;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-60
@@ -30,22 +30,22 @@ import ghidra.feature.vt.api.main.*;
|
|||||||
import ghidra.framework.data.OpenMode;
|
import ghidra.framework.data.OpenMode;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.*;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.xml.XmlUtilities;
|
import ghidra.util.xml.XmlUtilities;
|
||||||
|
|
||||||
public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
public class VTMatchSetDB extends DbObject implements VTMatchSet {
|
||||||
|
|
||||||
private final DBRecord matchSetRecord;
|
private final DBRecord matchSetRecord;
|
||||||
|
|
||||||
private DBObjectCache<VTMatchDB> matchCache;
|
private DbCache<VTMatchDB> matchCache;
|
||||||
private final VTSessionDB session;
|
private final VTSessionDB session;
|
||||||
private VTMatchTableDBAdapter matchTableAdapter;
|
private VTMatchTableDBAdapter matchTableAdapter;
|
||||||
|
|
||||||
@@ -73,13 +73,13 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private VTMatchSetDB(DBRecord record, VTSessionDB session, DBHandle dbHandle, Lock lock) {
|
private VTMatchSetDB(DBRecord record, VTSessionDB session, DBHandle dbHandle, Lock lock) {
|
||||||
super(null, record.getKey());// cache not supported
|
super(record.getKey());// cache not supported
|
||||||
this.matchSetRecord = record;
|
this.matchSetRecord = record;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.dbHandle = dbHandle;
|
this.dbHandle = dbHandle;
|
||||||
this.lock = lock;
|
this.lock = lock;
|
||||||
|
|
||||||
matchCache = new DBObjectCache<>(10);
|
matchCache = new DbCache<>(new MatchFactory(), lock, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTableAdapters(long tableID) throws IOException {
|
private void createTableAdapters(long tableID) throws IOException {
|
||||||
@@ -162,20 +162,17 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
VTAssociationDB associationDB = associationManager.getOrCreateAssociationDB(
|
VTAssociationDB associationDB = associationManager.getOrCreateAssociationDB(
|
||||||
info.getSourceAddress(), info.getDestinationAddress(), info.getAssociationType());
|
info.getSourceAddress(), info.getDestinationAddress(), info.getAssociationType());
|
||||||
VTMatchTag tag = info.getTag();
|
VTMatchTag tag = info.getTag();
|
||||||
VTMatch newMatch = null;
|
VTMatchDB newMatch = null;
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
VTMatchTagDB tagDB = session.getOrCreateMatchTagDB(tag);
|
VTMatchTagDB tagDB = session.getOrCreateMatchTagDB(tag);
|
||||||
DBRecord matchRecord =
|
DBRecord matchRecord =
|
||||||
matchTableAdapter.insertMatchRecord(info, this, associationDB, tagDB);
|
matchTableAdapter.insertMatchRecord(info, this, associationDB, tagDB);
|
||||||
newMatch = getMatchForRecord(matchRecord);
|
newMatch = new VTMatchDB(matchRecord, this);
|
||||||
|
matchCache.add(newMatch);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
if (newMatch != null) {
|
if (newMatch != null) {
|
||||||
session.setObjectChanged(VTEvent.MATCH_ADDED, newMatch, null, newMatch);
|
session.setObjectChanged(VTEvent.MATCH_ADDED, newMatch, null, newMatch);
|
||||||
}
|
}
|
||||||
@@ -213,8 +210,8 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
VTAssociation association = match.getAssociation();
|
VTAssociation association = match.getAssociation();
|
||||||
Address sourceAddress = association.getSourceAddress();
|
Address sourceAddress = association.getSourceAddress();
|
||||||
Address destinationAddress = association.getDestinationAddress();
|
Address destinationAddress = association.getDestinationAddress();
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
checkDeleted();
|
||||||
long matchKey = matchDb.getKey();
|
long matchKey = matchDb.getKey();
|
||||||
boolean deleted = matchTableAdapter.deleteRecord(matchKey);
|
boolean deleted = matchTableAdapter.deleteRecord(matchKey);
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
@@ -231,9 +228,6 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeletedMatch deletedMatch = new DeletedMatch(sourceAddress, destinationAddress);
|
DeletedMatch deletedMatch = new DeletedMatch(sourceAddress, destinationAddress);
|
||||||
session.setObjectChanged(VTEvent.MATCH_DELETED, match, deletedMatch, null);
|
session.setObjectChanged(VTEvent.MATCH_DELETED, match, deletedMatch, null);
|
||||||
@@ -247,35 +241,31 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
@Override
|
@Override
|
||||||
public Collection<VTMatch> getMatches() {
|
public Collection<VTMatch> getMatches() {
|
||||||
List<VTMatch> list = new LinkedList<>();
|
List<VTMatch> list = new LinkedList<>();
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
RecordIterator iterator = matchTableAdapter.getRecords();
|
RecordIterator iterator = matchTableAdapter.getRecords();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
DBRecord nextRecord = iterator.next();
|
DBRecord nextRecord = iterator.next();
|
||||||
list.add(getMatchForRecord(nextRecord));
|
list.add(matchCache.getCachedInstance(nextRecord));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VTMatch> getMatches(VTAssociation association) {
|
public Collection<VTMatch> getMatches(VTAssociation association) {
|
||||||
VTAssociationDB associationDB = (VTAssociationDB) association;
|
|
||||||
List<VTMatch> list = new LinkedList<>();
|
List<VTMatch> list = new LinkedList<>();
|
||||||
if (associationDB == null) {
|
try (Closeable c = lock.read()) {
|
||||||
return list; // No association, so no matches.
|
VTAssociationDB associationDB = (VTAssociationDB) association;
|
||||||
}
|
if (associationDB == null) {
|
||||||
try {
|
return list; // No association, so no matches.
|
||||||
|
}
|
||||||
RecordIterator iterator = matchTableAdapter.getRecords(associationDB.getKey());
|
RecordIterator iterator = matchTableAdapter.getRecords(associationDB.getKey());
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
DBRecord nextRecord = iterator.next();
|
DBRecord nextRecord = iterator.next();
|
||||||
VTMatch match = getMatchForRecord(nextRecord);
|
VTMatch match = matchCache.getCachedInstance(nextRecord);
|
||||||
list.add(match);
|
list.add(match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,44 +277,23 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VTMatch> getMatches(Address sourceAddress, Address destinationAddress) {
|
public Collection<VTMatch> getMatches(Address sourceAddress, Address destinationAddress) {
|
||||||
AssociationDatabaseManager associationManager = session.getAssociationManagerDBM();
|
try (Closeable c = lock.read()) {
|
||||||
VTAssociationDB existingAssociationDB =
|
AssociationDatabaseManager associationManager = session.getAssociationManagerDBM();
|
||||||
associationManager.getExistingAssociationDB(sourceAddress, destinationAddress);
|
VTAssociationDB existingAssociationDB =
|
||||||
if (existingAssociationDB == null) {
|
associationManager.getExistingAssociationDB(sourceAddress, destinationAddress);
|
||||||
return Collections.emptyList();
|
if (existingAssociationDB == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return getMatches(existingAssociationDB);
|
||||||
}
|
}
|
||||||
return getMatches(existingAssociationDB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean refresh() {
|
protected boolean refresh() {
|
||||||
|
// MatchSets are not cached, so this method is not used
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTMatch getMatchForRecord(DBRecord matchRecord) {
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
VTMatchDB match = matchCache.get(matchRecord);
|
|
||||||
if (match == null) {
|
|
||||||
match = new VTMatchDB(matchCache, matchRecord, this);
|
|
||||||
}
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DBRecord getMatchRecord(long matchRecordKey) {
|
|
||||||
try {
|
|
||||||
return matchTableAdapter.getMatchRecord(matchRecordKey);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
dbError(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Program getDestinationProgram() {
|
Program getDestinationProgram() {
|
||||||
return session.getDestinationProgram();
|
return session.getDestinationProgram();
|
||||||
}
|
}
|
||||||
@@ -345,9 +314,34 @@ public class VTMatchSetDB extends DatabaseObject implements VTMatchSet {
|
|||||||
matchCache.invalidate();
|
matchCache.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBRecord getMatchRecord(long matchKey) {
|
||||||
|
try {
|
||||||
|
return matchTableAdapter.getMatchRecord(matchKey);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
session.dbError(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Match Set " + getID() + " - " + getMatchCount() + " matches [Correlator=" +
|
return "Match Set " + getID() + " - " + getMatchCount() + " matches [Correlator=" +
|
||||||
getProgramCorrelatorInfo().getName() + "]";
|
getProgramCorrelatorInfo().getName() + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MatchFactory implements DbFactory<VTMatchDB> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VTMatchDB instantiate(long matchKey) {
|
||||||
|
DBRecord record = getMatchRecord(matchKey);
|
||||||
|
return record == null ? null : instantiate(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VTMatchDB instantiate(DBRecord rec) {
|
||||||
|
return new VTMatchDB(rec, VTMatchSetDB.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -15,25 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.feature.vt.api.db;
|
package ghidra.feature.vt.api.db;
|
||||||
|
|
||||||
import static ghidra.feature.vt.api.db.VTMatchTagDBAdapter.ColumnDescription.TAG_NAME_COL;
|
import static ghidra.feature.vt.api.db.VTMatchTagDBAdapter.ColumnDescription.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import db.DBRecord;
|
import db.DBRecord;
|
||||||
import ghidra.feature.vt.api.main.VTMatchTag;
|
import ghidra.feature.vt.api.main.VTMatchTag;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.database.DatabaseObject;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The database object for a user defined tag on a version tracking match.
|
* The database object for a user defined tag on a version tracking match.
|
||||||
*/
|
*/
|
||||||
public class VTMatchTagDB extends DatabaseObject implements VTMatchTag {
|
public class VTMatchTagDB extends DbObject implements VTMatchTag {
|
||||||
|
|
||||||
private VTSessionDB sessionDB;
|
private VTSessionDB sessionDB;
|
||||||
private DBRecord record;
|
private DBRecord record;
|
||||||
|
|
||||||
VTMatchTagDB(VTSessionDB sessionDB, DBObjectCache<VTMatchTagDB> cache, DBRecord record) {
|
VTMatchTagDB(VTSessionDB sessionDB, DBRecord record) {
|
||||||
super(cache, record.getKey());
|
super(record.getKey());
|
||||||
this.sessionDB = sessionDB;
|
this.sessionDB = sessionDB;
|
||||||
this.record = record;
|
this.record = record;
|
||||||
}
|
}
|
||||||
@@ -75,7 +74,7 @@ public class VTMatchTagDB extends DatabaseObject implements VTMatchTag {
|
|||||||
* null if the match tag has been deleted.
|
* null if the match tag has been deleted.
|
||||||
*/
|
*/
|
||||||
DBRecord getRecord() {
|
DBRecord getRecord() {
|
||||||
return checkIsValid() ? record : null;
|
return refreshIfNeeded() ? record : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+43
-86
@@ -32,12 +32,14 @@ import ghidra.framework.model.*;
|
|||||||
import ghidra.framework.model.TransactionInfo.Status;
|
import ghidra.framework.model.TransactionInfo.Status;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.framework.store.LockException;
|
import ghidra.framework.store.LockException;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DbCache;
|
||||||
|
import ghidra.program.database.DbFactory;
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -88,7 +90,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
private VTMatchSetTableDBAdapter matchSetTableAdapter;
|
private VTMatchSetTableDBAdapter matchSetTableAdapter;
|
||||||
private AssociationDatabaseManager associationManager;
|
private AssociationDatabaseManager associationManager;
|
||||||
private VTMatchTagDBAdapter matchTagAdapter;
|
private VTMatchTagDBAdapter matchTagAdapter;
|
||||||
private DBObjectCache<VTMatchTagDB> tagCache = new DBObjectCache<>(10);
|
private DbCache<VTMatchTagDB> tagCache;
|
||||||
|
|
||||||
private Program sourceProgram;
|
private Program sourceProgram;
|
||||||
private Program destinationProgram;
|
private Program destinationProgram;
|
||||||
@@ -127,7 +129,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
public VTSessionDB(String name, Program sourceProgram, Program destinationProgram,
|
public VTSessionDB(String name, Program sourceProgram, Program destinationProgram,
|
||||||
Object consumer) throws IOException {
|
Object consumer) throws IOException {
|
||||||
super(new DBHandle(), UNUSED_DEFAULT_NAME, EVENT_NOTIFICATION_DELAY, consumer);
|
super(new DBHandle(), UNUSED_DEFAULT_NAME, EVENT_NOTIFICATION_DELAY, consumer);
|
||||||
|
tagCache = new DbCache<>(new TagFactory(), lock, 10);
|
||||||
propertyTable = dbh.getTable(PROPERTY_TABLE_NAME);
|
propertyTable = dbh.getTable(PROPERTY_TABLE_NAME);
|
||||||
|
|
||||||
int ID = startTransaction("Constructing New Version Tracking Match Set");
|
int ID = startTransaction("Constructing New Version Tracking Match Set");
|
||||||
@@ -435,16 +437,12 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearCache(boolean all) {
|
protected void clearCache(boolean all) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
super.clearCache(all);
|
super.clearCache(all);
|
||||||
associationManager.invalidateCache();
|
associationManager.invalidateCache();
|
||||||
tagCache.invalidate();
|
tagCache.invalidate();
|
||||||
reconcileCachedMatchSets();
|
reconcileCachedMatchSets();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -512,14 +510,10 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTMatchSet createMatchSet(VTProgramCorrelator correlator) {
|
public VTMatchSet createMatchSet(VTProgramCorrelator correlator) {
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
long id = matchSetTableAdapter.getNextMatchSetID();
|
long id = matchSetTableAdapter.getNextMatchSetID();
|
||||||
return createMatchSet(correlator, id);
|
return createMatchSet(correlator, id);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTMatchSet createMatchSet(VTProgramCorrelator correlator, long id) {
|
private VTMatchSet createMatchSet(VTProgramCorrelator correlator, long id) {
|
||||||
@@ -588,7 +582,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VTMatchSet> getMatchSets() {
|
public List<VTMatchSet> getMatchSets() {
|
||||||
return new ArrayList<>(matchSetMap.values());
|
return lock.withRead(() -> new ArrayList<>(matchSetMap.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSet getSourceAddressSet(DBRecord record) throws IOException {
|
AddressSet getSourceAddressSet(DBRecord record) throws IOException {
|
||||||
@@ -649,17 +643,13 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<VTMatch> getMatches(VTAssociation association) {
|
public List<VTMatch> getMatches(VTAssociation association) {
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
List<VTMatch> matches = new ArrayList<>();
|
List<VTMatch> matches = new ArrayList<>();
|
||||||
for (VTMatchSet matchSet : matchSetMap.values()) {
|
for (VTMatchSet matchSet : matchSetMap.values()) {
|
||||||
matches.addAll(matchSet.getMatches(association));
|
matches.addAll(matchSet.getMatches(association));
|
||||||
}
|
}
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -679,30 +669,22 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTMatchSet getManualMatchSet() {
|
public VTMatchSet getManualMatchSet() {
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
if (manualMatchSet == null) {
|
if (manualMatchSet == null) {
|
||||||
manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName());
|
manualMatchSet = findMatchSet(ManualMatchProgramCorrelator.class.getName());
|
||||||
}
|
}
|
||||||
return manualMatchSet;
|
return manualMatchSet;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTMatchSet getImpliedMatchSet() {
|
public VTMatchSet getImpliedMatchSet() {
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
if (impliedMatchSet == null) {
|
if (impliedMatchSet == null) {
|
||||||
impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName());
|
impliedMatchSet = findMatchSet(ImpliedMatchProgramCorrelator.class.getName());
|
||||||
}
|
}
|
||||||
return impliedMatchSet;
|
return impliedMatchSet;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTMatchSet findMatchSet(String correlatorClassName) {
|
private VTMatchSet findMatchSet(String correlatorClassName) {
|
||||||
@@ -719,8 +701,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
@Override
|
@Override
|
||||||
public void deleteMatchTag(VTMatchTag tag) {
|
public void deleteMatchTag(VTMatchTag tag) {
|
||||||
String tagName = tag.getName();
|
String tagName = tag.getName();
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
VTMatchTagDB tagDB = getMatchTagDB(tagName);
|
VTMatchTagDB tagDB = getMatchTagDB(tagName);
|
||||||
if (tagDB == null) {
|
if (tagDB == null) {
|
||||||
return; // not sure if this can happen
|
return; // not sure if this can happen
|
||||||
@@ -734,30 +715,24 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
setObjectChanged(VTEvent.TAG_REMOVED, this, tagName, null);
|
setObjectChanged(VTEvent.TAG_REMOVED, this, tagName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VTMatchTagDB createMatchTag(String tagName) {
|
public VTMatchTagDB createMatchTag(String tagName) {
|
||||||
VTMatchTagDB matchTag = null;
|
VTMatchTagDB matchTag = null;
|
||||||
try {
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
matchTag = getMatchTagDB(tagName);
|
matchTag = getMatchTagDB(tagName);
|
||||||
if (matchTag != null) {
|
if (matchTag != null) {
|
||||||
return matchTag;
|
return matchTag;
|
||||||
}
|
}
|
||||||
DBRecord record = matchTagAdapter.insertRecord(tagName);
|
DBRecord record = matchTagAdapter.insertRecord(tagName);
|
||||||
matchTag = new VTMatchTagDB(this, tagCache, record);
|
matchTag = new VTMatchTagDB(this, record);
|
||||||
|
tagCache.add(matchTag);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
setObjectChanged(VTEvent.TAG_ADDED, matchTag, null, matchTag);
|
setObjectChanged(VTEvent.TAG_ADDED, matchTag, null, matchTag);
|
||||||
return matchTag;
|
return matchTag;
|
||||||
}
|
}
|
||||||
@@ -773,66 +748,28 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VTMatchTag getMatchTag(long tagKey) {
|
||||||
|
VTMatchTagDB tag = tagCache.getCachedInstance(tagKey);
|
||||||
|
return tag != null ? tag : VTMatchTag.UNTAGGED;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<VTMatchTag> getMatchTags() {
|
public Set<VTMatchTag> getMatchTags() {
|
||||||
Set<VTMatchTag> tags = new HashSet<>();
|
Set<VTMatchTag> tags = new HashSet<>();
|
||||||
try {
|
try (Closeable c = lock.read()) {
|
||||||
lock.acquire();
|
|
||||||
RecordIterator records = matchTagAdapter.getRecords();
|
RecordIterator records = matchTagAdapter.getRecords();
|
||||||
while (records.hasNext()) {
|
while (records.hasNext()) {
|
||||||
DBRecord record = records.next();
|
DBRecord record = records.next();
|
||||||
tags.add(getMatchTagNew(record));
|
tags.add(tagCache.getCachedInstance(record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VTMatchTagDB getMatchTagNew(DBRecord record) {
|
|
||||||
if (record == null) {
|
|
||||||
throw new AssertException("How can we have a null record?!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
VTMatchTagDB matchTagDB = tagCache.get(record);
|
|
||||||
if (matchTagDB == null) {
|
|
||||||
matchTagDB = new VTMatchTagDB(this, tagCache, record);
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchTagDB;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VTMatchTag getMatchTag(long key) {
|
|
||||||
try {
|
|
||||||
lock.acquire();
|
|
||||||
VTMatchTagDB matchTagDB = tagCache.get(key);
|
|
||||||
if (matchTagDB != null) {
|
|
||||||
return matchTagDB;
|
|
||||||
}
|
|
||||||
DBRecord record = matchTagAdapter.getRecord(key);
|
|
||||||
if (record != null) {
|
|
||||||
return new VTMatchTagDB(this, tagCache, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
dbError(e);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return VTMatchTag.UNTAGGED;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBRecord getTagRecord(long key) throws IOException {
|
DBRecord getTagRecord(long key) throws IOException {
|
||||||
return matchTagAdapter.getRecord(key);
|
return matchTagAdapter.getRecord(key);
|
||||||
}
|
}
|
||||||
@@ -875,4 +812,24 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||||||
associationManager.dispose();
|
associationManager.dispose();
|
||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TagFactory implements DbFactory<VTMatchTagDB> {
|
||||||
|
@Override
|
||||||
|
public VTMatchTagDB instantiate(long key) {
|
||||||
|
try {
|
||||||
|
DBRecord record = matchTagAdapter.getRecord(key);
|
||||||
|
return record == null ? null : instantiate(record);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
dbError(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VTMatchTagDB instantiate(DBRecord record) {
|
||||||
|
return new VTMatchTagDB(VTSessionDB.this, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@ import ghidra.feature.vt.api.markuptype.*;
|
|||||||
import ghidra.feature.vt.api.util.Stringable;
|
import ghidra.feature.vt.api.util.Stringable;
|
||||||
import ghidra.feature.vt.api.util.VersionTrackingApplyException;
|
import ghidra.feature.vt.api.util.VersionTrackingApplyException;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DbObject;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
@@ -402,7 +402,7 @@ public class MarkupItemImpl implements VTMarkupItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStoredInDB() {
|
public boolean isStoredInDB() {
|
||||||
return (markupItemStorage instanceof DatabaseObject);
|
return (markupItemStorage instanceof DbObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+193
@@ -0,0 +1,193 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package generic.cache;
|
||||||
|
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.map.LRUMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic cache implementation that removes items from the cache when they are no longer
|
||||||
|
* referenced. It uses weak references for the values and a small hard cache
|
||||||
|
* so that recent items don't get garbage collected immediately.
|
||||||
|
* <P>
|
||||||
|
* This class is thread safe. All public methods are synchronized.
|
||||||
|
*
|
||||||
|
* @param <K> The type of the object stored in this cache
|
||||||
|
* @param <V> The type of the object stored in this cache
|
||||||
|
*/
|
||||||
|
public class WeakReferenceCache<K, V> {
|
||||||
|
|
||||||
|
private Map<K, KeyedReference<K, V>> refsById;
|
||||||
|
private ReferenceQueue<V> refQueue;
|
||||||
|
private LRUMap<K, V> hardCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new WeakReferenceCache with a given hard cache size. The hard cache size is
|
||||||
|
* the minimum number of objects to keep in the cache. Typically, the cache will contain
|
||||||
|
* more than this number, but the excess objects are subject to garbage collection.
|
||||||
|
* @param hardCacheSize the minimum number of objects to keep in the cache.
|
||||||
|
*/
|
||||||
|
public WeakReferenceCache(int hardCacheSize) {
|
||||||
|
refsById = new HashMap<>();
|
||||||
|
refQueue = new ReferenceQueue<>();
|
||||||
|
|
||||||
|
hardCache = new LRUMap<>(hardCacheSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the database object with the given key from the cache.
|
||||||
|
* @param key the key of the object to retrieve.
|
||||||
|
* @return the cached object or null if the object with that key is not currently cached.
|
||||||
|
*/
|
||||||
|
public synchronized V get(K key) {
|
||||||
|
KeyedReference<K, V> ref = refsById.get(key);
|
||||||
|
if (ref == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
V v = ref.get();
|
||||||
|
if (v == null) {
|
||||||
|
refsById.remove(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
hardCache.put(key, v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of objects currently in the cache.
|
||||||
|
* @return the number of objects currently in the cache.
|
||||||
|
*/
|
||||||
|
public synchronized int size() {
|
||||||
|
return refsById.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of objects to protect against garbage collection.
|
||||||
|
* @param size the minimum number of objects to keep in the cache.
|
||||||
|
*/
|
||||||
|
public synchronized void setHardCacheSize(int size) {
|
||||||
|
hardCache.clear();
|
||||||
|
hardCache = new LRUMap<>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given database object to the cache.
|
||||||
|
* @param key the key for the cached object
|
||||||
|
* @param data the object to add to the cache
|
||||||
|
* @return the object that has been added to the cache
|
||||||
|
*/
|
||||||
|
public synchronized V add(K key, V data) {
|
||||||
|
processQueue();
|
||||||
|
hardCache.put(key, data);
|
||||||
|
KeyedReference<K, V> ref = new KeyedReference<>(key, data, refQueue);
|
||||||
|
refsById.put(key, ref);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an List of all the cached objects.
|
||||||
|
* @return an List of all the cached objects.
|
||||||
|
*/
|
||||||
|
public synchronized List<V> getCachedObjects() {
|
||||||
|
List<V> list = new ArrayList<>();
|
||||||
|
processQueue();
|
||||||
|
for (KeyedReference<K, V> ref : refsById.values()) {
|
||||||
|
V v = ref.get();
|
||||||
|
if (v != null) {
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the given consumer to all values in the cache.
|
||||||
|
* @param consumer the consumer to apply to all values in the cache
|
||||||
|
*/
|
||||||
|
public synchronized void apply(Consumer<V> consumer) {
|
||||||
|
processQueue();
|
||||||
|
for (KeyedReference<K, V> ref : refsById.values()) {
|
||||||
|
V v = ref.get();
|
||||||
|
if (v != null) {
|
||||||
|
consumer.accept(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the object with the given key from the cache.
|
||||||
|
* @param key the key of the object to remove
|
||||||
|
* @return the value that was removed from the cache
|
||||||
|
*/
|
||||||
|
public synchronized V delete(K key) {
|
||||||
|
processQueue();
|
||||||
|
KeyedReference<K, V> ref = refsById.remove(key);
|
||||||
|
V v = null;
|
||||||
|
if (ref != null) {
|
||||||
|
v = ref.get();
|
||||||
|
ref.clear();
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deleteIf(Predicate<V> predicate) {
|
||||||
|
Iterator<KeyedReference<K, V>> iterator = refsById.values().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
KeyedReference<K, V> ref = iterator.next();
|
||||||
|
V v = ref.get();
|
||||||
|
if (v == null || predicate.test(v)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we know the cast is safe--we put them in there
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void processQueue() {
|
||||||
|
KeyedReference<K, V> ref;
|
||||||
|
while ((ref = (KeyedReference<K, V>) refQueue.poll()) != null) {
|
||||||
|
K key = ref.getKey();
|
||||||
|
KeyedReference<K, V> oldValue = refsById.remove(key);
|
||||||
|
|
||||||
|
if (oldValue != null && oldValue != ref) {
|
||||||
|
// we have put another item in the cache with the same key. Further, we
|
||||||
|
// removed the item, but the garbage collector had not put the item on the
|
||||||
|
// reference queue until after we added a new reference to the cache.
|
||||||
|
// We want to keep the last value that was added, as it has not been deleted.
|
||||||
|
refsById.put(key, oldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class KeyedReference<K, V> extends WeakReference<V> {
|
||||||
|
private K key;
|
||||||
|
|
||||||
|
KeyedReference(K key, V obj, ReferenceQueue<V> queue) {
|
||||||
|
super(obj, queue);
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
-29
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -16,14 +16,16 @@
|
|||||||
package ghidra.framework.data;
|
package ghidra.framework.data;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import generic.timer.GhidraTimer;
|
import generic.timer.GhidraTimer;
|
||||||
import generic.timer.GhidraTimerFactory;
|
import generic.timer.GhidraTimerFactory;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||||
import ghidra.util.datastruct.WeakSet;
|
import ghidra.util.datastruct.WeakSet;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to queue and send {@link DomainObjectChangeRecord} events.
|
* A class to queue and send {@link DomainObjectChangeRecord} events.
|
||||||
@@ -225,35 +227,14 @@ class DomainObjectChangeSupport {
|
|||||||
//=================================================================================================
|
//=================================================================================================
|
||||||
|
|
||||||
// Note: all clients of lockQueue() must not call external APIs that could use locking
|
// Note: all clients of lockQueue() must not call external APIs that could use locking
|
||||||
private void withLock(Runnable r) {
|
private void withLock(Callback r) {
|
||||||
|
writeLock.withWrite(r);
|
||||||
try {
|
|
||||||
writeLock.acquire();
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
writeLock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: all clients of lockQueue() must not call external APIs that could use locking
|
// Note: all clients of lockQueue() must not call external APIs that could use locking
|
||||||
private <T> T withLock(Callable<T> c) {
|
private <T> T withLock(Supplier<T> s) {
|
||||||
|
try (Closeable c = writeLock.write()) {
|
||||||
try {
|
return s.get();
|
||||||
writeLock.acquire();
|
|
||||||
T result;
|
|
||||||
try {
|
|
||||||
result = c.call();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
// sholudn't happen
|
|
||||||
Msg.error(this, "Exception while updating change records", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
writeLock.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -15,14 +15,41 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.util;
|
package ghidra.util;
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ghidra synchronization lock. This class allows creation of named locks for
|
* Ghidra synchronization read/write lock that provides read and write methods for acquiring either
|
||||||
* synchronizing modification of multiple tables in the Ghidra database.
|
* a shared read access or an exclusive write access.
|
||||||
|
* <P>
|
||||||
|
* It extends Java's ReentrantReadWriteLock, but
|
||||||
|
* adds convenient methods to get either a read or write lock that returns an {@link AutoCloseable}
|
||||||
|
* object that can be used in a try with resources block that will auto release the lock when
|
||||||
|
* the try block is exited.
|
||||||
|
* <P>
|
||||||
|
* To use a lock for share read access the general pattern is something like:
|
||||||
|
* <PRE>
|
||||||
|
* int getSize() {
|
||||||
|
* try (Closeable c = lock.read()) {
|
||||||
|
* return record.getIntValue(SIZE);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </PRE>
|
||||||
|
* <P>
|
||||||
|
* Similarly, to get exclusive access for modification:
|
||||||
|
* <PRE>
|
||||||
|
* int getSize() {
|
||||||
|
* try (Closeable c = lock.write()) {
|
||||||
|
* return record.setIntValue(SIZE);
|
||||||
|
* database.updateRecord(record);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </PRE>
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class Lock {
|
public class Lock extends ReentrantReadWriteLock {
|
||||||
private Thread owner;
|
|
||||||
private int lockAquireCount = 0;
|
|
||||||
private int waiterCount = 0;
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,58 +61,34 @@ public class Lock {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Acquire this synchronization lock. (i.e. begin synchronizing on this named
|
public String toString() {
|
||||||
* lock.)
|
return name + " Lock";
|
||||||
*/
|
|
||||||
public synchronized void acquire() {
|
|
||||||
Thread currThread = Thread.currentThread();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (owner == null) {
|
|
||||||
lockAquireCount = 1;
|
|
||||||
owner = currThread;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (owner == currThread) {
|
|
||||||
lockAquireCount++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
waiterCount++;
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
// exception from another threads notify(), ignore
|
|
||||||
// and try to get lock again
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
waiterCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases this lock, since you are through with the code that needed
|
* Acquires the read lock that can allow simultaneous access by all read threads. Will block
|
||||||
* synchronization.
|
* if any thread already has a write lock.
|
||||||
|
* @return An AutoCloseable handle to the lock that be used in a try with resources block to
|
||||||
|
* automatically release the lock when the block is exited.
|
||||||
*/
|
*/
|
||||||
public synchronized void release() {
|
public Closeable read() {
|
||||||
Thread currThread = Thread.currentThread();
|
super.readLock().lock();
|
||||||
|
return () -> super.readLock().unlock();
|
||||||
|
|
||||||
if (lockAquireCount > 0 && (owner == currThread)) {
|
}
|
||||||
if (--lockAquireCount == 0) {
|
|
||||||
owner = null;
|
/**
|
||||||
// This is purely to help sample profiling. If notify() is called the
|
* Acquires the exclusive write lock that prevents any other thread, reader or writer, from
|
||||||
// sampler can attribute time to the methods calling this erroneously. For some reason
|
* getting a lock while the write lock is held. Will block if any other thread has either
|
||||||
// the visualvm sampler gets a sample more often when notify() is called.
|
* a read or write lock. VERY IMPORTANT, any thread that attempts to acquire the write lock
|
||||||
if (waiterCount != 0) {
|
* while already holding the read lock, will cause an immediate deadlock.
|
||||||
notify();
|
* @return An AutoCloseable handle to the lock that be used in a try with resources block to
|
||||||
}
|
* automatically release the lock when the block is exited.
|
||||||
}
|
*/
|
||||||
}
|
public Closeable write() {
|
||||||
else {
|
super.writeLock().lock();
|
||||||
throw new IllegalStateException("Attempted to release an unowned lock: " + name);
|
return () -> super.writeLock().unlock();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,7 +96,50 @@ public class Lock {
|
|||||||
*
|
*
|
||||||
* @return the thread that owns the lock or null.
|
* @return the thread that owns the lock or null.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public Thread getOwner() {
|
public Thread getOwner() {
|
||||||
return owner;
|
return super.getOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for acquiring a read lock, executing a supplier object,
|
||||||
|
* then releasing the lock.
|
||||||
|
* @param <T> the supplier return type
|
||||||
|
* @param supplier the supplier to execute while holding a read lock.
|
||||||
|
* @return the result from the supplier
|
||||||
|
*/
|
||||||
|
public <T> T withRead(Supplier<T> supplier) {
|
||||||
|
readLock().lock();
|
||||||
|
try {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method for acquiring a write lock, executing a callback object,
|
||||||
|
* then releasing the lock.
|
||||||
|
* @param callback the callback to execute while holding a write lock.
|
||||||
|
*/
|
||||||
|
public void withWrite(Callback callback) {
|
||||||
|
writeLock().lock();
|
||||||
|
try {
|
||||||
|
callback.call();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object to auto close an acquired read or write lock. Note that we can't just use the
|
||||||
|
* java {@link java.io.Closeable} because it throws an exception on the close call that
|
||||||
|
* we don't want.
|
||||||
|
*/
|
||||||
|
public interface Closeable extends AutoCloseable {
|
||||||
|
@Override
|
||||||
|
public void close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-337
@@ -1,337 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.program.database;
|
|
||||||
|
|
||||||
import java.lang.ref.ReferenceQueue;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import db.DBRecord;
|
|
||||||
import ghidra.program.model.address.KeyRange;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic cache implementation for objects that extend DatabaseObject. This is a reference based
|
|
||||||
* cache such that objects are only ever automatically removed from the cache when there are no
|
|
||||||
* references to that object. It also maintains a small "hard" cache so that recently accessed objects
|
|
||||||
* are not prematurely removed from the cache if there are no references to them.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the object stored in this cache
|
|
||||||
*/
|
|
||||||
public class DBObjectCache<T extends DatabaseObject> {
|
|
||||||
|
|
||||||
private Map<Long, KeyedSoftReference<T>> map;
|
|
||||||
private ReferenceQueue<T> refQueue;
|
|
||||||
private LinkedList<T> hardCache;
|
|
||||||
private int hardCacheSize;
|
|
||||||
private volatile int invalidateCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new DBObjectCache with a given hard cache size. The hard cache size is
|
|
||||||
* the minimum number of objects to keep in the cache. Typically, the cache will contain
|
|
||||||
* more than this number, but the excess objects are subject to garbage collections
|
|
||||||
* @param hardCacheSize the minimum number of objects to keep in the cache.
|
|
||||||
*/
|
|
||||||
public DBObjectCache(int hardCacheSize) {
|
|
||||||
this.hardCacheSize = hardCacheSize;
|
|
||||||
map = new HashMap<>();
|
|
||||||
refQueue = new ReferenceQueue<>();
|
|
||||||
hardCache = new LinkedList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the database object with the given key from the cache.
|
|
||||||
* @param key the key of the object to retrieve.
|
|
||||||
* @return the cached object or null if the object with that key is not currently cached.
|
|
||||||
*/
|
|
||||||
public synchronized T get(long key) {
|
|
||||||
KeyedSoftReference<T> ref = map.get(key);
|
|
||||||
if (ref != null) {
|
|
||||||
T obj = ref.get();
|
|
||||||
if (obj == null) {
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (obj.checkIsValid()) {
|
|
||||||
addToHardCache(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the database object with the given record and associated key from the cache.
|
|
||||||
* This form should be used in conjunction with record iterators to avoid unnecessary
|
|
||||||
* record query during a possible object refresh. To benefit from the record the cached
|
|
||||||
* object must implement the {@link DatabaseObject#refresh(DBRecord)} method which by default
|
|
||||||
* ignores the record and simply calls {@link DatabaseObject#refresh()}.
|
|
||||||
* @param objectRecord the valid record corresponding to the object to be retrieved and possibly
|
|
||||||
* used to refresh the associated object if found in cache
|
|
||||||
* @return the cached object or null if the object with that key is not currently cached.
|
|
||||||
*/
|
|
||||||
public synchronized T get(DBRecord objectRecord) {
|
|
||||||
long key = objectRecord.getKey();
|
|
||||||
KeyedSoftReference<T> ref = map.get(key);
|
|
||||||
if (ref != null) {
|
|
||||||
T obj = ref.get();
|
|
||||||
if (obj == null) {
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (obj.checkIsValid(objectRecord)) {
|
|
||||||
addToHardCache(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of objects currently in the cache.
|
|
||||||
* @return the number of objects currently in the cache.
|
|
||||||
*/
|
|
||||||
public int size() {
|
|
||||||
return map.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the number of objects to protect against garbage collection.
|
|
||||||
* @param size the minimum number of objects to keep in the cache.
|
|
||||||
*/
|
|
||||||
public synchronized void setHardCacheSize(int size) {
|
|
||||||
while (hardCache.size() > size) {
|
|
||||||
hardCache.removeLast();
|
|
||||||
}
|
|
||||||
this.hardCacheSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the given database object to the cache.
|
|
||||||
* @param data the object to add to the cache.
|
|
||||||
*/
|
|
||||||
void put(T data) {
|
|
||||||
processQueue();
|
|
||||||
long key = data.getKey();
|
|
||||||
addToHardCache(data);
|
|
||||||
KeyedSoftReference<T> ref = new KeyedSoftReference<>(key, data, refQueue);
|
|
||||||
map.put(key, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an List of all the cached objects.
|
|
||||||
* @return an List of all the cached objects.
|
|
||||||
*/
|
|
||||||
public synchronized List<T> getCachedObjects() {
|
|
||||||
ArrayList<T> list = new ArrayList<>();
|
|
||||||
processQueue();
|
|
||||||
for (KeyedSoftReference<T> ref : map.values()) {
|
|
||||||
T obj = ref.get();
|
|
||||||
if (obj != null) {
|
|
||||||
list.add(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all objects from HashMap whose key is contained
|
|
||||||
* within the specified keyRanges.
|
|
||||||
* @param keyRanges key ranges to delete
|
|
||||||
*/
|
|
||||||
//TODO: Discourage large cases by only allowing a single range to be specified
|
|
||||||
public synchronized void delete(List<KeyRange> keyRanges) {
|
|
||||||
hardCache.clear();
|
|
||||||
processQueue();
|
|
||||||
long rangesSize = getKeyRangesSize(keyRanges); // < 0 too many ranges
|
|
||||||
if (rangesSize < 0 || rangesSize > map.size()) {
|
|
||||||
deleteLargeKeyRanges(keyRanges);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
deleteSmallKeyRanges(keyRanges);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all objects from cache whose key is contained
|
|
||||||
* within the specified keyRanges. Iteration over all
|
|
||||||
* keys contained within keyRanges will be performed.
|
|
||||||
* @param keyRanges key ranges to delete
|
|
||||||
*/
|
|
||||||
private void deleteSmallKeyRanges(List<KeyRange> keyRanges) {
|
|
||||||
for (KeyRange range : keyRanges) {
|
|
||||||
for (long key = range.minKey; key <= range.maxKey; key++) {
|
|
||||||
KeyedSoftReference<T> ref = map.remove(key);
|
|
||||||
if (ref != null) {
|
|
||||||
DatabaseObject obj = ref.get();
|
|
||||||
if (obj != null) {
|
|
||||||
obj.setDeleted();
|
|
||||||
ref.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all objects from cache whose key is contained
|
|
||||||
* within the specified keyRanges. Iteration over all
|
|
||||||
* keys contained within map will be performed.
|
|
||||||
* @param keyRanges key ranges to delete
|
|
||||||
*/
|
|
||||||
private void deleteLargeKeyRanges(List<KeyRange> keyRanges) {
|
|
||||||
map.values().removeIf(ref -> checkRef(ref, keyRanges));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkRef(KeyedSoftReference<T> ref, List<KeyRange> keyRanges) {
|
|
||||||
long key = ref.getKey();
|
|
||||||
if (keyRangesContain(keyRanges, key)) {
|
|
||||||
DatabaseObject obj = ref.get();
|
|
||||||
if (obj != null) {
|
|
||||||
obj.setDeleted();
|
|
||||||
ref.clear();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return total number of keys covered by list of keyRanges.
|
|
||||||
* @param keyRanges key ranges to get the number of keys
|
|
||||||
* @return number of keys, or -1 if more than Long.MAX_VALUE keys
|
|
||||||
*/
|
|
||||||
private long getKeyRangesSize(List<KeyRange> keyRanges) {
|
|
||||||
long size = 0;
|
|
||||||
for (KeyRange range : keyRanges) {
|
|
||||||
size += range.length();
|
|
||||||
if (size < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean keyRangesContain(List<KeyRange> keyRanges, long key) {
|
|
||||||
for (KeyRange range : keyRanges) {
|
|
||||||
if (range.contains(key)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks all the cached objects as invalid. Invalid objects will have to refresh themselves
|
|
||||||
* before they are allowed to be used. If an invalidated object cannot refresh itself, then
|
|
||||||
* the object is removed from the cache and discarded and the application can no longer use
|
|
||||||
* that instance of the object.
|
|
||||||
*/
|
|
||||||
public synchronized void invalidate() {
|
|
||||||
hardCache.clear();
|
|
||||||
processQueue();
|
|
||||||
if (++invalidateCount <= 0) {
|
|
||||||
invalidateCount = 1;
|
|
||||||
for (KeyedSoftReference<T> ref : map.values()) {
|
|
||||||
DatabaseObject obj = ref.get();
|
|
||||||
if (obj != null) {
|
|
||||||
obj.setInvalid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current invalidate counter value which corresponds to the number of time
|
|
||||||
* the entire cache has been invalidated.
|
|
||||||
* @return current invalidate counter value.
|
|
||||||
*/
|
|
||||||
int getInvalidateCount() {
|
|
||||||
return invalidateCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the object with the given key from the cache.
|
|
||||||
* @param key the key of the object to remove.
|
|
||||||
*/
|
|
||||||
public synchronized void delete(long key) {
|
|
||||||
processQueue();
|
|
||||||
KeyedSoftReference<T> ref = map.get(key);
|
|
||||||
if (ref != null) {
|
|
||||||
T obj = ref.get();
|
|
||||||
if (obj != null) {
|
|
||||||
obj.setDeleted();
|
|
||||||
ref.clear();
|
|
||||||
}
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addToHardCache(T obj) {
|
|
||||||
hardCache.addLast(obj);
|
|
||||||
if (hardCache.size() > hardCacheSize) {
|
|
||||||
hardCache.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we know the cast is safe--we put them in there
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void processQueue() {
|
|
||||||
KeyedSoftReference<T> ref;
|
|
||||||
while ((ref = (KeyedSoftReference<T>) refQueue.poll()) != null) {
|
|
||||||
long key = ref.getKey();
|
|
||||||
KeyedSoftReference<T> oldValue = map.remove(key);
|
|
||||||
|
|
||||||
if (oldValue != null && oldValue != ref) {
|
|
||||||
// we have put another item in the cache with the same key. Further, we
|
|
||||||
// removed the item, but the garbage collector had not put the item on the
|
|
||||||
// reference queue until after we added a new reference to the cache.
|
|
||||||
// We want to keep the last value that was added, as it has not been deleted.
|
|
||||||
map.put(key, oldValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class KeyedSoftReference<T> extends WeakReference<T> {
|
|
||||||
private long key;
|
|
||||||
|
|
||||||
KeyedSoftReference(long key, T obj, ReferenceQueue<T> queue) {
|
|
||||||
super(obj, queue);
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
long getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void keyChanged(long oldKey, long newKey) {
|
|
||||||
processQueue();
|
|
||||||
|
|
||||||
KeyedSoftReference<T> ref = map.remove(oldKey);
|
|
||||||
if (ref != null) {
|
|
||||||
map.put(newKey, ref);
|
|
||||||
T t = ref.get();
|
|
||||||
if (t != null) {
|
|
||||||
t.setInvalid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-5
@@ -33,6 +33,7 @@ import ghidra.program.model.listing.Program;
|
|||||||
import ghidra.program.util.ProgramChangeRecord;
|
import ghidra.program.util.ProgramChangeRecord;
|
||||||
import ghidra.program.util.ProgramEvent;
|
import ghidra.program.util.ProgramEvent;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -496,14 +497,10 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearCache(boolean all) {
|
protected void clearCache(boolean all) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
super.clearCache(all);
|
super.clearCache(all);
|
||||||
dataTypeManager.invalidateCache();
|
dataTypeManager.invalidateCache();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
-231
@@ -1,231 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.program.database;
|
|
||||||
|
|
||||||
import java.util.ConcurrentModificationException;
|
|
||||||
|
|
||||||
import db.DBRecord;
|
|
||||||
import ghidra.util.Lock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for an cached object in the database. Database objects have keys. They are marked as
|
|
||||||
* invalid when a database cache is cleared and can be revived on a refresh as long as they haven't
|
|
||||||
* been deleted. Instantiating an object will cause it to be added immediately to the associated
|
|
||||||
* cache.
|
|
||||||
*/
|
|
||||||
public abstract class DatabaseObject {
|
|
||||||
|
|
||||||
protected long key;
|
|
||||||
private volatile boolean deleted;
|
|
||||||
@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.
|
|
||||||
*
|
|
||||||
* @param cache to be used for this object or null if object will not be cached
|
|
||||||
* @param key database key to uniquely identify this object
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected DatabaseObject(@SuppressWarnings("rawtypes") DBObjectCache cache, long key) {
|
|
||||||
this.key = key;
|
|
||||||
this.cache = cache;
|
|
||||||
if (cache != null) {
|
|
||||||
cache.put(this);
|
|
||||||
this.invalidateCount = cache.getInvalidateCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the database key for this object.
|
|
||||||
*/
|
|
||||||
public long getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the object as deleted.
|
|
||||||
*/
|
|
||||||
protected void setDeleted() {
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Invalidate this object. This does not necessarily mean that this object can never be used
|
|
||||||
* again. If the object can refresh itself, it may still be useable.
|
|
||||||
*/
|
|
||||||
public void setInvalid() {
|
|
||||||
invalidateCount = getCurrentValidationCount() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setValid() {
|
|
||||||
invalidateCount = getCurrentValidationCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCurrentValidationCount() {
|
|
||||||
return cache != null ? cache.getInvalidateCount() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void keyChanged(long newKey) {
|
|
||||||
long oldKey = key;
|
|
||||||
this.key = newKey;
|
|
||||||
if (cache != null) {
|
|
||||||
cache.keyChanged(oldKey, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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()},
|
|
||||||
* {@link #validate(Lock)} and {@link #isDeleted(Lock)} are methods which will force
|
|
||||||
* a re-validation if required.
|
|
||||||
*
|
|
||||||
* @return true if this object is invalid and must be re-validated, else false if object state
|
|
||||||
* is currently valid which may include a deleted state.
|
|
||||||
*/
|
|
||||||
protected final boolean isInvalid() {
|
|
||||||
return !deleted && invalidateCount != getCurrentValidationCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this object has been deleted, in which case any use of the object is not allowed.
|
|
||||||
* This method should be invoked before any modifications to the object are performed to
|
|
||||||
* ensure it still exists and is in a valid state.
|
|
||||||
*
|
|
||||||
* @throws ConcurrentModificationException if the object has been deleted from the database.
|
|
||||||
*/
|
|
||||||
protected void checkDeleted() {
|
|
||||||
if (!checkIsValid()) {
|
|
||||||
throw new ConcurrentModificationException("Object has been deleted.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this object is still valid. If the object is invalid, the object will attempt
|
|
||||||
* to refresh itself. If the refresh fails, the object will be marked as deleted.
|
|
||||||
*
|
|
||||||
* @return true if the object is valid, else false if deleted
|
|
||||||
*/
|
|
||||||
protected boolean checkIsValid() {
|
|
||||||
return checkIsValid(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether this object is still valid. If the object is invalid and not deleted, the
|
|
||||||
* object will attempt to refresh itself using the specified record. If the refresh fails,
|
|
||||||
* the object will be marked as deleted and removed from cache. If this object is already
|
|
||||||
* marked as deleted a refresh will not be perormed.
|
|
||||||
* <P>
|
|
||||||
* This method may invoke {@link #refresh(DBRecord)} to perform a refresh. It is important
|
|
||||||
* that such a refresh avoid recursing back into this method.
|
|
||||||
*
|
|
||||||
* @param record optional record which may be used to refresh invalid object
|
|
||||||
* @return true if the object is valid or false if it has been deleted.
|
|
||||||
*/
|
|
||||||
protected boolean checkIsValid(DBRecord record) {
|
|
||||||
if (isInvalid()) {
|
|
||||||
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();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
refreshing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
|
|
||||||
* 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 or false if deleted
|
|
||||||
*/
|
|
||||||
protected boolean validate(Lock lock) {
|
|
||||||
if (!isInvalid()) {
|
|
||||||
return !deleted;
|
|
||||||
}
|
|
||||||
lock.acquire();
|
|
||||||
try {
|
|
||||||
return checkIsValid();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this object has been deleted. Note: once an object has been deleted, it will
|
|
||||||
* never be "refreshed". For example, if an object is ever deleted and is resurrected via an
|
|
||||||
* "undo", you will have get a fresh instance of the object.
|
|
||||||
*
|
|
||||||
* @param lock object cache lock object
|
|
||||||
* @return true if this object has been deleted.
|
|
||||||
*/
|
|
||||||
public boolean isDeleted(Lock lock) {
|
|
||||||
return deleted || !validate(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the object to refresh its state from the database.
|
|
||||||
*
|
|
||||||
* @return true if the object was able to refresh itself. Return false if the object was
|
|
||||||
* deleted. Objects that extend this class must implement a refresh method. If an object
|
|
||||||
* can never refresh itself, then it should always return false.
|
|
||||||
*/
|
|
||||||
protected abstract boolean refresh();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the object to refresh its state from the database using the specified record if not
|
|
||||||
* null. NOTE: The default implementation ignores the record and invokes refresh().
|
|
||||||
* Implementations of this method must take care if multiple database tables are used since the
|
|
||||||
* record supplied could correspond to another object. In some cases it may be best not to
|
|
||||||
* override this method or ignore the record provided.
|
|
||||||
*
|
|
||||||
* @param record valid record associated with object's key (optional, may be null to force
|
|
||||||
* record lookup or other refresh technique)
|
|
||||||
* @return true if the object was able to refresh itself. Return false if record is null and
|
|
||||||
* object was deleted. Objects that extend this class must implement a refresh method.
|
|
||||||
* If an object can never refresh itself, then it should always return false.
|
|
||||||
*/
|
|
||||||
protected boolean refresh(DBRecord record) {
|
|
||||||
return refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,388 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.program.database;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import db.DBRecord;
|
||||||
|
import generic.cache.WeakReferenceCache;
|
||||||
|
import ghidra.program.model.address.KeyRange;
|
||||||
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DbObject cache that efficiently manages the use of the database lock and uses a
|
||||||
|
* factory to create objects in the cache when they are not present.
|
||||||
|
* <P>
|
||||||
|
* This version of the cache is designed to be used without first acquiring a database lock. It will
|
||||||
|
* first attempt to retrieve the object without acquiring the lock. If that fails, it will acquire
|
||||||
|
* the lock, attempt to refresh the object if necessary or possibly create a new instance using
|
||||||
|
* the factory and add it to the cache.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the database object
|
||||||
|
*/
|
||||||
|
public class DbCache<T extends DbObject> {
|
||||||
|
public static final DbCache<?> DUMMY = new DummyCache();
|
||||||
|
public static final int INVALID_COUNT = -1;
|
||||||
|
protected Lock lock;
|
||||||
|
protected DbFactory<T> factory;
|
||||||
|
protected WeakReferenceCache<Long, T> refCache;
|
||||||
|
private volatile int modificationCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param factory the factory for creating new instances of database objects
|
||||||
|
* @param lock the database lock.
|
||||||
|
* @param hardCacheSize the size of the hard reference cache
|
||||||
|
*/
|
||||||
|
public DbCache(DbFactory<T> factory, Lock lock, int hardCacheSize) {
|
||||||
|
this.refCache = new WeakReferenceCache<>(hardCacheSize);
|
||||||
|
this.factory = factory;
|
||||||
|
this.lock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DbCache() {
|
||||||
|
// do nothing (For Dummy)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of objects currently in the cache. A database lock is not required.
|
||||||
|
* @return the number of objects currently in the cache.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return refCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given database object to the cache.
|
||||||
|
* @param dbObject the object to add to the cache.
|
||||||
|
* @return the object that was cached
|
||||||
|
*/
|
||||||
|
public T add(T dbObject) {
|
||||||
|
dbObject.setCache(this);
|
||||||
|
return refCache.add(dbObject.getKey(), dbObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the database object with the given key from the cache. This differs from the
|
||||||
|
* super's get() method in that it does not require that the database lock be acquired prior
|
||||||
|
* to calling this method and in fact performs better if the lock is not acquired. This version
|
||||||
|
* will first look in the cache and if it finds one that is definitely valid (there is
|
||||||
|
* a quick check that doesn't require the lock), it returns it immediately. Otherwise, the
|
||||||
|
* cache doesn't have the object or the object needs refreshing. In this case, this method
|
||||||
|
* will acquire the lock, attempt refresh the object if it exists, and if necessary call a
|
||||||
|
* factory method to create a new instance and add it to the cache.
|
||||||
|
*
|
||||||
|
* @param key the key of the object to retrieve.
|
||||||
|
* @return the cached object or null if the object with that key is not currently cached.
|
||||||
|
*/
|
||||||
|
public T getCachedInstance(Long key) {
|
||||||
|
T t = refCache.get(key);
|
||||||
|
if (t != null && t.isValid()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Closeable c = lock.read()) {
|
||||||
|
if (t != null && t.refreshIfNeeded()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// note that the instantiateAndCacheSafely is synchronized and it will double check the cache
|
||||||
|
// to prevent multiple threads from getting here and then creating a dbObject.
|
||||||
|
return instantiateAndCacheSafely(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the object from the cache, but only if it already exists and is valid in the cache.
|
||||||
|
* It will not attempt to refresh the object or call the factory method to create new instances.
|
||||||
|
*
|
||||||
|
* @param key the key of the object to retrieve.
|
||||||
|
* @return the cached object or null if the object with that key is not currently cached and
|
||||||
|
* valid.
|
||||||
|
*/
|
||||||
|
public T getIfValid(Long key) {
|
||||||
|
T t = refCache.get(key);
|
||||||
|
if (t != null && t.isValid()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the object with the given key from the cache with checking if it is valid or needs
|
||||||
|
* to be refreshed. It will not use the factory to create new instances if they don't exist.
|
||||||
|
* This method is used in very specialized situations where the caller has already done
|
||||||
|
* checking and they know the status of the object and how to refresh it cheaply if needed.
|
||||||
|
* @param key the key of the object to retrieve
|
||||||
|
* @return the object directly from the cache if one exists in the cache.
|
||||||
|
*/
|
||||||
|
public T getRaw(Long key) {
|
||||||
|
return refCache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the database object with the given record and associated key from the cache.
|
||||||
|
* This form should be used in conjunction with record iterators to avoid unnecessary
|
||||||
|
* record query during a possible object refresh. To benefit from the record the cached
|
||||||
|
* object must implement the {@link DbObject#refresh(DBRecord)} method which by default
|
||||||
|
* ignores the record and simply calls {@link DbObject#refresh()}.
|
||||||
|
* <P>
|
||||||
|
* This method is similar to the get() method in that it can be called without the database
|
||||||
|
* lock. It will first check if the object is in the cache and definitely valid before
|
||||||
|
* acquiring the lock and refreshing or creating the database object.
|
||||||
|
* @param dbRecord the valid record corresponding to the object to be retrieved and possibly
|
||||||
|
* used to refresh the associated object if found in cache
|
||||||
|
* @return the cached object or null if the object with that key is not currently cached.
|
||||||
|
*/
|
||||||
|
public T getCachedInstance(DBRecord dbRecord) {
|
||||||
|
if (dbRecord == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long key = dbRecord.getKey();
|
||||||
|
T t = refCache.get(key);
|
||||||
|
if (t != null && t.isValid()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
try (Closeable c = lock.read()) {
|
||||||
|
if (t != null && t.refreshIfNeeded(dbRecord)) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// note that the instantiateAndCacheSafely is synchronized and it will double check the cache
|
||||||
|
// to prevent multiple threads from getting here and then creating a dbObject.
|
||||||
|
return instantiateAndCacheSafely(dbRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an List of all the cached objects. These objects have not been checked to see if they
|
||||||
|
* are valid or not.
|
||||||
|
* @return an List of all the cached objects.
|
||||||
|
*/
|
||||||
|
public List<T> getCachedObjects() {
|
||||||
|
return refCache.getCachedObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all objects from HashMap whose key is contained
|
||||||
|
* within the specified keyRanges.
|
||||||
|
* @param keyRanges key ranges to delete
|
||||||
|
*/
|
||||||
|
public void delete(List<KeyRange> keyRanges) {
|
||||||
|
long rangesSize = getKeyRangesSize(keyRanges); // < 0 too many ranges
|
||||||
|
if (rangesSize < 0 || rangesSize > refCache.size()) {
|
||||||
|
deleteLargeKeyRanges(keyRanges);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
deleteSmallKeyRanges(keyRanges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all the cached objects as invalid. Invalid objects will have to refresh themselves
|
||||||
|
* before they are allowed to be used. If an invalidated object cannot refresh itself, then
|
||||||
|
* the object is removed from the cache and discarded and the application can no longer use
|
||||||
|
* that instance of the object.
|
||||||
|
*/
|
||||||
|
public void invalidate() {
|
||||||
|
// Note: the ++ operation here is not atomic, but that shouldn't be an issue. If more
|
||||||
|
// than one thread attempts to increment the count at the same time, it is possible for
|
||||||
|
// the count not to be incremented the full number of times, but it will be incremented
|
||||||
|
// at least by 1, which is all that really matters.
|
||||||
|
if (++modificationCount < 0) {
|
||||||
|
// if it overflows, reset to 0 so it is never negative
|
||||||
|
modificationCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the object with the given key from the cache. A database lock is not required.
|
||||||
|
* @param key the key of the object to remove.
|
||||||
|
*/
|
||||||
|
public void delete(long key) {
|
||||||
|
T deleted = refCache.delete(key);
|
||||||
|
if (deleted != null) {
|
||||||
|
deleted.setDeleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the cache for an object whose key has changed.
|
||||||
|
* @param oldKey the old key
|
||||||
|
* @param newKey the new key
|
||||||
|
*/
|
||||||
|
public void keyChanged(long oldKey, long newKey) {
|
||||||
|
synchronized (refCache) {
|
||||||
|
T t = refCache.delete(oldKey);
|
||||||
|
if (t != null) {
|
||||||
|
t.setInvalid();
|
||||||
|
refCache.add(newKey, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current cache modification counter value which corresponds to the number of times
|
||||||
|
* the entire cache has been invalidated.
|
||||||
|
* @return the current modification counter value
|
||||||
|
*/
|
||||||
|
public int getModificationCount() {
|
||||||
|
return modificationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized T instantiateAndCacheSafely(Long key) {
|
||||||
|
// check if another thread got here first
|
||||||
|
T t = refCache.get(key);
|
||||||
|
if (t != null && t.refreshIfNeeded()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = factory.instantiate(key);
|
||||||
|
if (t != null) {
|
||||||
|
add(t);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized T instantiateAndCacheSafely(DBRecord record) {
|
||||||
|
// check if another thread got here first
|
||||||
|
T t = refCache.get(record.getKey());
|
||||||
|
if (t != null && t.refreshIfNeeded()) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
t = factory.instantiate(record);
|
||||||
|
add(t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all objects from cache whose key is contained
|
||||||
|
* within the specified keyRanges. Iteration over all
|
||||||
|
* keys contained within keyRanges will be performed.
|
||||||
|
* @param keyRanges key ranges to delete
|
||||||
|
*/
|
||||||
|
private void deleteSmallKeyRanges(List<KeyRange> keyRanges) {
|
||||||
|
synchronized (refCache) {
|
||||||
|
for (KeyRange range : keyRanges) {
|
||||||
|
for (long key = range.minKey; key <= range.maxKey; key++) {
|
||||||
|
T dbObject = refCache.delete(key);
|
||||||
|
if (dbObject != null) {
|
||||||
|
dbObject.setDeleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all objects from cache whose key is contained
|
||||||
|
* within the specified keyRanges. Iteration over all
|
||||||
|
* keys contained within map will be performed.
|
||||||
|
* @param keyRanges key ranges to delete
|
||||||
|
*/
|
||||||
|
private void deleteLargeKeyRanges(List<KeyRange> keyRanges) {
|
||||||
|
refCache.deleteIf(v -> checkInRange(v, keyRanges));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkInRange(T t, List<KeyRange> keyRanges) {
|
||||||
|
long key = t.getKey();
|
||||||
|
if (keyRangesContain(keyRanges, key)) {
|
||||||
|
t.setDeleted();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return total number of keys covered by list of keyRanges.
|
||||||
|
* @param keyRanges key ranges to get the number of keys
|
||||||
|
* @return number of keys, or -1 if more than Long.MAX_VALUE keys
|
||||||
|
*/
|
||||||
|
private long getKeyRangesSize(List<KeyRange> keyRanges) {
|
||||||
|
long size = 0;
|
||||||
|
for (KeyRange range : keyRanges) {
|
||||||
|
size += range.length();
|
||||||
|
if (size < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean keyRangesContain(List<KeyRange> keyRanges, long key) {
|
||||||
|
for (KeyRange range : keyRanges) {
|
||||||
|
if (range.contains(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DummyCache extends DbCache<DbObject> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getModificationCount() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DbObject getCachedInstance(Long key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DbObject getIfValid(Long key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DbObject getCachedInstance(DBRecord dbRecord) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DbObject add(DbObject dbObject) {
|
||||||
|
return dbObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyChanged(long oldKey, long newKey) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(long key) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(List<KeyRange> keyRanges) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DbObject> getCachedObjects() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.program.database;
|
||||||
|
|
||||||
|
import db.DBRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for Factories that create {@link DbObject}s. Required by the {@link DbCache}
|
||||||
|
* <P>
|
||||||
|
* NOTE: This factory is used to instantiate dbObject instances that are already backed
|
||||||
|
* by records in the database. In general, these methods should only be used by the
|
||||||
|
* DbCache class to insure that the objects aren't already cached if they are created using
|
||||||
|
* this factor, they are added to the cache in a thread safe way.
|
||||||
|
*
|
||||||
|
* @param <T> the type of database object
|
||||||
|
*/
|
||||||
|
public interface DbFactory<T extends DbObject> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DatabaseObject of type T for the given key. It is
|
||||||
|
* expected that a record for this object already exists in the database. This method is
|
||||||
|
* simply creating the unique instance that is associated with that record.
|
||||||
|
* @param key the database key the database key
|
||||||
|
* @return the newly created instance of type T or null if no record found
|
||||||
|
*/
|
||||||
|
public T instantiate(long key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DatabaseObject of type T for the given record.
|
||||||
|
* @param record the database record to create its associated unique instance of T
|
||||||
|
* @return the newly created instance of type T
|
||||||
|
*/
|
||||||
|
public T instantiate(DBRecord record);
|
||||||
|
|
||||||
|
}
|
||||||
+286
@@ -0,0 +1,286 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.program.database;
|
||||||
|
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
|
||||||
|
import db.DBRecord;
|
||||||
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for objects stored in the database.
|
||||||
|
* <P>
|
||||||
|
* The general contract for database objects is that there should only ever be one instance for
|
||||||
|
* a specific database object at any given time. To facilitate this, instances of database objects
|
||||||
|
* should be stored in a {@link DbCache} and the cache should be queried before creating
|
||||||
|
* any new instances.
|
||||||
|
* <P>
|
||||||
|
* Database objects have keys that are used by the database record and also serves as the key for
|
||||||
|
* cache lookup. They are marked as invalid when a database cache is invalidated and can be revived
|
||||||
|
* on a refresh as long as they haven't been deleted.
|
||||||
|
*/
|
||||||
|
public abstract class DbObject {
|
||||||
|
|
||||||
|
protected long key;
|
||||||
|
private volatile boolean deleted;
|
||||||
|
private DbCache<?> cache = DbCache.DUMMY;
|
||||||
|
private volatile int lastValidModificationCount;
|
||||||
|
private boolean refreshing = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DatabaseObject and adds it to the specified cache.
|
||||||
|
*
|
||||||
|
* @param key database key to uniquely identify this object
|
||||||
|
*/
|
||||||
|
protected DbObject(long key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a special method for setting the cache and should ONLY be used by the
|
||||||
|
* {@link DbCache} when an object is added to the cache.
|
||||||
|
* @param cache the cache this object was added to.
|
||||||
|
*/
|
||||||
|
void setCache(DbCache<?> cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
this.lastValidModificationCount = cache.getModificationCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the database key for this object.
|
||||||
|
* @return the database key for this object.
|
||||||
|
*/
|
||||||
|
public long getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the object as deleted.
|
||||||
|
*/
|
||||||
|
protected void setDeleted() {
|
||||||
|
deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Invalidate this object. This does not necessarily mean that this object can never be used
|
||||||
|
* again. If the object can refresh itself, it may still be usable.
|
||||||
|
*/
|
||||||
|
public void setInvalid() {
|
||||||
|
lastValidModificationCount = DbCache.INVALID_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this object as valid. Note that this call does no checking on its own should be used
|
||||||
|
* very carefully and only when the caller is absolutely sure that the object is valid. Used
|
||||||
|
* in a special case for default symbols that have no corresponding record.
|
||||||
|
*/
|
||||||
|
protected void setValid() {
|
||||||
|
lastValidModificationCount = cache.getModificationCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for updating the cache if a object record key changed. This is a very unusual
|
||||||
|
* situation and should generally be avoided.
|
||||||
|
* @param newKey the new key for the associated record for this object
|
||||||
|
*/
|
||||||
|
protected void keyChanged(long newKey) {
|
||||||
|
long oldKey = key;
|
||||||
|
this.key = newKey;
|
||||||
|
cache.keyChanged(oldKey, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the object needs to be refreshed prior to further use. An object needs
|
||||||
|
* to be refreshed if the cache has been invalidated since the last time the object was
|
||||||
|
* checked for validity. There is a special case if the object has been deleted. It certainly
|
||||||
|
* doesn't need to be refreshed but clients that have the object already in hand still want to
|
||||||
|
* be able to call its getter methods without encountering an error. The assumption is that
|
||||||
|
* eventually the client will be kicked so it knows it needs to re-aquire its objects.
|
||||||
|
* <P>
|
||||||
|
* An object that has not been deleted may be marked as stale and in need of a refresh in one of
|
||||||
|
* two ways. The cache was invalidated or the object was specifically marked as invalid via its
|
||||||
|
* {@link #setInvalid()} method. A common situation where this can occur is an undo/redo
|
||||||
|
* operation against the underlying database. The methods {@link #refreshIfNeeded()},
|
||||||
|
* {@link #checkDeleted()}, {@link #validate(Lock)} and {@link #isDeleted(Lock)} are methods
|
||||||
|
* which will force a re-validation if required.
|
||||||
|
* <P>
|
||||||
|
* This method is final as it is intended to be a "fast" check that doesn't require a lock
|
||||||
|
* to see if the object is possibly in need of a refresh. We don't want any subclass to add
|
||||||
|
* additional checks that might require a database lock. The idea is that if the cache
|
||||||
|
* modification hasn't changed, then we know the object is valid. If the modification count
|
||||||
|
* has changed then we may or may not be valid and stronger checks are needed which will require
|
||||||
|
* a lock. Subclasses should override {@link #refreshIfNeeded()}, {@link #refresh()} to do
|
||||||
|
* the more robust validation checking.
|
||||||
|
*
|
||||||
|
* @return true if this object is invalid and must be re-validated, else false if object state
|
||||||
|
* is currently valid which may include a deleted state.
|
||||||
|
*/
|
||||||
|
protected final boolean needsRefreshing() {
|
||||||
|
if (deleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return lastValidModificationCount != cache.getModificationCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the object is known to be valid at this time. Note that this is is subtly
|
||||||
|
* different from being the inverse of {@link #needsRefreshing()}, specifically in the case
|
||||||
|
* where the object has been deleted. For a deleted object, the {@link #isValid} method
|
||||||
|
* will return false, but the {@link #needsRefreshing()} will also return false.
|
||||||
|
* @return true if the object is known to be valid at this time.
|
||||||
|
*/
|
||||||
|
public final boolean isValid() {
|
||||||
|
if (deleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return lastValidModificationCount == cache.getModificationCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the object's state from the database if it is possibly stale. The check to see
|
||||||
|
* if an object is stale is fast, so this generally is a quick call unless an undo or
|
||||||
|
* redo has taken place and a true refresh is required. If a refresh is performed, the
|
||||||
|
* object may be discovered to be deleted. This method should only be called while
|
||||||
|
* holding a {@link Lock#readLock()}
|
||||||
|
*
|
||||||
|
* @return true if the object is valid, else false if deleted
|
||||||
|
*/
|
||||||
|
protected boolean refreshIfNeeded() {
|
||||||
|
return refreshIfNeeded(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the object's state if it is stale using the given record.
|
||||||
|
* If the object has already been deleted, it will immediately return false. Otherwise, it
|
||||||
|
* will check to see if it needs to be refreshed, and if so, refresh it. While refreshing the
|
||||||
|
* object, it may be discovered that it has been deleted and marked as such.
|
||||||
|
* @param record a known valid record the object can use to refresh itself, if a refresh is
|
||||||
|
* needed. Null is permitted, in which case the object will have to do a database lookup to
|
||||||
|
* retrieve a record if a refresh is needed.
|
||||||
|
*
|
||||||
|
* @return true if the object is valid, else false if deleted
|
||||||
|
*/
|
||||||
|
synchronized boolean refreshIfNeeded(DBRecord record) {
|
||||||
|
if (needsRefreshing()) {
|
||||||
|
doRefresh(record);
|
||||||
|
}
|
||||||
|
return !deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this object has been deleted, in which case any use of the object is not allowed.
|
||||||
|
* This method should be invoked before any modifications to the object are performed to
|
||||||
|
* ensure it still exists and is in a valid state.
|
||||||
|
*
|
||||||
|
* @throws ConcurrentModificationException if the object has been deleted from the database.
|
||||||
|
*/
|
||||||
|
protected void checkDeleted() {
|
||||||
|
if (!refreshIfNeeded(null)) {
|
||||||
|
throw new ConcurrentModificationException("Object has been deleted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method for performing a refresh on a database object. This method may be called
|
||||||
|
* recursively, which is can detect and short circuit.
|
||||||
|
* @param record a known valid record the object can use to refresh itself or null. If null
|
||||||
|
* the object will have to do its own database retrieval of its record.
|
||||||
|
*/
|
||||||
|
private void doRefresh(DBRecord 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;
|
||||||
|
}
|
||||||
|
refreshing = true;
|
||||||
|
try {
|
||||||
|
if (refresh(record)) {
|
||||||
|
// Object is valid
|
||||||
|
setValid();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if refresh failed, object has been deleted
|
||||||
|
cache.delete(key);
|
||||||
|
setDeleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
refreshing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
|
||||||
|
* 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 or false if deleted
|
||||||
|
*/
|
||||||
|
protected boolean validate(Lock lock) {
|
||||||
|
if (isValid()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (deleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Closeable c = lock.read()) {
|
||||||
|
return refreshIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this object has been deleted. Note: once an object has been deleted, it will
|
||||||
|
* never be "refreshed". For example, if an object is ever deleted and is resurrected via an
|
||||||
|
* "undo", you will have get a fresh instance of the object.
|
||||||
|
*
|
||||||
|
* @param lock object cache lock object
|
||||||
|
* @return true if this object has been deleted.
|
||||||
|
*/
|
||||||
|
public boolean isDeleted(Lock lock) {
|
||||||
|
return deleted || !validate(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the object to refresh its state from the database.
|
||||||
|
*
|
||||||
|
* @return true if the object was able to refresh itself. Return false if the object was
|
||||||
|
* deleted. Objects that extend this class must implement a refresh method. If an object
|
||||||
|
* can never refresh itself, then it should always return false.
|
||||||
|
*/
|
||||||
|
protected abstract boolean refresh();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the object to refresh its state from the database using the specified record if not
|
||||||
|
* null. NOTE: The default implementation ignores the record and invokes refresh().
|
||||||
|
* Implementations of this method must take care if multiple database tables are used since the
|
||||||
|
* record supplied could correspond to another object. In some cases it may be best not to
|
||||||
|
* override this method or ignore the record provided.
|
||||||
|
*
|
||||||
|
* @param record valid record associated with object's key (optional, may be null to force
|
||||||
|
* record lookup or other refresh technique)
|
||||||
|
* @return true if the object was able to refresh itself. Return false if record is null and
|
||||||
|
* object was deleted. Objects that extend this class must implement a refresh method.
|
||||||
|
* If an object can never refresh itself, then it should always return false.
|
||||||
|
*/
|
||||||
|
protected boolean refresh(DBRecord record) {
|
||||||
|
return refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+19
-57
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -24,6 +24,7 @@ import ghidra.program.database.util.AddressRangeMapDB;
|
|||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.util.ProgramEvent;
|
import ghidra.program.util.ProgramEvent;
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -42,24 +43,19 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
public static IntRangeMapDB getPropertyMap(ProgramDB program, String mapName,
|
public static IntRangeMapDB getPropertyMap(ProgramDB program, String mapName,
|
||||||
ErrorHandler errHandler, AddressMap addrMap, Lock lock) {
|
ErrorHandler errHandler, AddressMap addrMap, Lock lock) {
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
DBHandle dbh = program.getDBHandle();
|
DBHandle dbh = program.getDBHandle();
|
||||||
String tableName = IntRangeMapDB.TABLE_PREFIX + mapName;
|
String tableName = IntRangeMapDB.TABLE_PREFIX + mapName;
|
||||||
if (dbh.getTable(tableName) != null) {
|
if (dbh.getTable(tableName) != null) {
|
||||||
return new IntRangeMapDB(program, mapName, program, addrMap, lock);
|
return new IntRangeMapDB(program, mapName, program, addrMap, lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IntRangeMapDB createPropertyMap(ProgramDB program, String mapName,
|
public static IntRangeMapDB createPropertyMap(ProgramDB program, String mapName,
|
||||||
ErrorHandler errHandler, AddressMap addrMap, Lock lock) throws DuplicateNameException {
|
ErrorHandler errHandler, AddressMap addrMap, Lock lock) throws DuplicateNameException {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
DBHandle dbh = program.getDBHandle();
|
DBHandle dbh = program.getDBHandle();
|
||||||
String tableName = TABLE_PREFIX + mapName;
|
String tableName = TABLE_PREFIX + mapName;
|
||||||
if (dbh.getTable(tableName) != null) {
|
if (dbh.getTable(tableName) != null) {
|
||||||
@@ -69,9 +65,6 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
|
|
||||||
return new IntRangeMapDB(program, mapName, program, addrMap, lock);
|
return new IntRangeMapDB(program, mapName, program, addrMap, lock);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntRangeMapDB(ProgramDB program, String mapName, ErrorHandler errHandler,
|
private IntRangeMapDB(ProgramDB program, String mapName, ErrorHandler errHandler,
|
||||||
@@ -99,23 +92,16 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
@Override
|
@Override
|
||||||
public void setValue(Address start, Address end, int value) {
|
public void setValue(Address start, Address end, int value) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
propertyMap.paintRange(start, end, new IntField(value));
|
propertyMap.paintRange(start, end, new IntField(value));
|
||||||
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setValue(AddressSetView addresses, int value) {
|
public void setValue(AddressSetView addresses, int value) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
try (Closeable c = lock.write()) {
|
||||||
lock.acquire();
|
|
||||||
try {
|
|
||||||
AddressRangeIterator iter = addresses.getAddressRanges();
|
AddressRangeIterator iter = addresses.getAddressRanges();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
AddressRange range = iter.next();
|
AddressRange range = iter.next();
|
||||||
@@ -123,42 +109,30 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
}
|
}
|
||||||
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearAll() {
|
public void clearAll() {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
propertyMap.dispose();
|
propertyMap.dispose();
|
||||||
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearValue(Address startAddr, Address endAddr) {
|
public void clearValue(Address startAddr, Address endAddr) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
propertyMap.clearRange(startAddr, endAddr);
|
propertyMap.clearRange(startAddr, endAddr);
|
||||||
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearValue(AddressSetView addresses) {
|
public void clearValue(AddressSetView addresses) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
AddressRangeIterator iter = addresses.getAddressRanges();
|
AddressRangeIterator iter = addresses.getAddressRanges();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
AddressRange range = iter.next();
|
AddressRange range = iter.next();
|
||||||
@@ -166,49 +140,34 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
}
|
}
|
||||||
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
program.setChanged(ProgramEvent.INT_PROPERTY_MAP_CHANGED, null, mapName);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getValue(Address address) {
|
public Integer getValue(Address address) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
Field value = propertyMap.getValue(address);
|
Field value = propertyMap.getValue(address);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ((IntField) value).getIntValue();
|
return ((IntField) value).getIntValue();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSet getAddressSet() {
|
public AddressSet getAddressSet() {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
return propertyMap.getAddressSet();
|
return propertyMap.getAddressSet();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSet getAddressSet(int value) {
|
public AddressSet getAddressSet(int value) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
return propertyMap.getAddressSet(new IntField(value));
|
return propertyMap.getAddressSet(new IntField(value));
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -216,13 +175,16 @@ public class IntRangeMapDB implements IntRangeMap {
|
|||||||
* @param fromAddr move from address
|
* @param fromAddr move from address
|
||||||
* @param toAddr move to address
|
* @param toAddr move to address
|
||||||
* @param length number of address to move
|
* @param length number of address to move
|
||||||
* @param monitor
|
* @param monitor the task monitor
|
||||||
* @throws CancelledException
|
* @throws CancelledException if cancelled
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
propertyMap.moveAddressRange(fromAddr, toAddr, length, monitor);
|
checkDeleted();
|
||||||
|
try (Closeable c = lock.write()) {
|
||||||
|
propertyMap.moveAddressRange(fromAddr, toAddr, length, monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-85
@@ -59,6 +59,7 @@ import ghidra.program.model.util.AddressSetPropertyMap;
|
|||||||
import ghidra.program.model.util.PropertyMapManager;
|
import ghidra.program.model.util.PropertyMapManager;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.Lock.Closeable;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@@ -1160,8 +1161,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setName(String newName) {
|
public void setName(String newName) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
if (name.equals(newName)) {
|
if (name.equals(newName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1172,9 +1172,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshName() throws IOException {
|
private void refreshName() throws IOException {
|
||||||
@@ -1206,8 +1203,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProgramOverlayAddressSpace ovSpace = null;
|
ProgramOverlayAddressSpace ovSpace = null;
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
if (imageBaseOverride) {
|
if (imageBaseOverride) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Overlay spaces may not be created while an image-base override is active");
|
"Overlay spaces may not be created while an image-base override is active");
|
||||||
@@ -1220,9 +1216,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return ovSpace;
|
return ovSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,8 +1223,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
public void renameOverlaySpace(String overlaySpaceName, String newName)
|
public void renameOverlaySpace(String overlaySpaceName, String newName)
|
||||||
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
|
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
checkExclusiveAccess();
|
checkExclusiveAccess();
|
||||||
|
|
||||||
AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName);
|
AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName);
|
||||||
@@ -1253,16 +1245,12 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeOverlaySpace(String overlaySpaceName)
|
public boolean removeOverlaySpace(String overlaySpaceName)
|
||||||
throws LockException, NotFoundException {
|
throws LockException, NotFoundException {
|
||||||
|
|
||||||
lock.acquire();
|
|
||||||
try {
|
try {
|
||||||
checkExclusiveAccess();
|
checkExclusiveAccess();
|
||||||
|
|
||||||
@@ -1286,9 +1274,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1321,8 +1306,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
if (commit) {
|
if (commit) {
|
||||||
checkExclusiveAccess();
|
checkExclusiveAccess();
|
||||||
}
|
}
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
Address currentImageBase = getImageBase();
|
Address currentImageBase = getImageBase();
|
||||||
if (!(commit && imageBaseOverride) && base.equals(currentImageBase)) {
|
if (!(commit && imageBaseOverride) && base.equals(currentImageBase)) {
|
||||||
return;
|
return;
|
||||||
@@ -1382,9 +1366,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
//NOTE:
|
//NOTE:
|
||||||
//this needs to be outside the lock...
|
//this needs to be outside the lock...
|
||||||
((TreeManager) managers[TREE_MGR]).imageBaseChanged(commit);
|
((TreeManager) managers[TREE_MGR]).imageBaseChanged(commit);
|
||||||
@@ -1396,14 +1377,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
if (!imageBaseOverride) {
|
if (!imageBaseOverride) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
imageBaseOverride = false;
|
imageBaseOverride = false;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
//NOTE:
|
//NOTE:
|
||||||
//this needs to be outside the lock...
|
//this needs to be outside the lock...
|
||||||
((TreeManager) managers[TREE_MGR]).imageBaseChanged(false);
|
((TreeManager) managers[TREE_MGR]).imageBaseChanged(false);
|
||||||
@@ -1856,8 +1833,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearCache(boolean all) {
|
protected void clearCache(boolean all) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
super.clearCache(all);
|
super.clearCache(all);
|
||||||
refreshName();
|
refreshName();
|
||||||
overlaySpaceAdapter.updateOverlaySpaces(addressFactory);
|
overlaySpaceAdapter.updateOverlaySpaces(addressFactory);
|
||||||
@@ -1872,9 +1848,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dbError(e);
|
dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1944,8 +1917,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
throws RollbackException {
|
throws RollbackException {
|
||||||
|
|
||||||
// TODO: ensure that managers are notified with address ranges which correspond to a sequential set of address keys
|
// TODO: ensure that managers are notified with address ranges which correspond to a sequential set of address keys
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
for (int i = NUM_MANAGERS - 1; i >= 0; i--) {
|
for (int i = NUM_MANAGERS - 1; i >= 0; i--) {
|
||||||
managers[i].deleteAddressRange(startAddr, endAddr, monitor);
|
managers[i].deleteAddressRange(startAddr, endAddr, monitor);
|
||||||
}
|
}
|
||||||
@@ -1967,9 +1939,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
throw new RollbackException("Operation cancelled");
|
throw new RollbackException("Operation cancelled");
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1989,8 +1958,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
|
|
||||||
// TODO: WARNING! fromAddr range may no longer exist in memory map which could affect certain database iterators
|
// TODO: WARNING! fromAddr range may no longer exist in memory map which could affect certain database iterators
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
for (int i = NUM_MANAGERS - 1; i >= 0; i--) {
|
for (int i = NUM_MANAGERS - 1; i >= 0; i--) {
|
||||||
managers[i].moveAddressRange(fromAddr, toAddr, length, monitor);
|
managers[i].moveAddressRange(fromAddr, toAddr, length, monitor);
|
||||||
}
|
}
|
||||||
@@ -2010,9 +1978,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
throw new RollbackException("Operation cancelled");
|
throw new RollbackException("Operation cancelled");
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2057,8 +2022,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
|
|
||||||
checkExclusiveAccess();
|
checkExclusiveAccess();
|
||||||
|
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
setEventsEnabled(false);
|
setEventsEnabled(false);
|
||||||
try {
|
try {
|
||||||
boolean redisassemblyRequired = true;
|
boolean redisassemblyRequired = true;
|
||||||
@@ -2167,9 +2131,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
}
|
}
|
||||||
fireEvent(new DomainObjectChangeRecord(ProgramEvent.LANGUAGE_CHANGED));
|
fireEvent(new DomainObjectChangeRecord(ProgramEvent.LANGUAGE_CHANGED));
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2281,23 +2243,18 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
@Override
|
@Override
|
||||||
public AddressSetPropertyMap createAddressSetPropertyMap(String mapName)
|
public AddressSetPropertyMap createAddressSetPropertyMap(String mapName)
|
||||||
throws DuplicateNameException {
|
throws DuplicateNameException {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
AddressSetPropertyMapDB map =
|
AddressSetPropertyMapDB map =
|
||||||
AddressSetPropertyMapDB.createPropertyMap(this, mapName, this, addrMap, lock);
|
AddressSetPropertyMapDB.createPropertyMap(this, mapName, this, addrMap, lock);
|
||||||
addrSetPropertyMap.put(mapName, map);
|
addrSetPropertyMap.put(mapName, map);
|
||||||
setChanged(ProgramEvent.ADDRESS_PROPERTY_MAP_ADDED, null, mapName);
|
setChanged(ProgramEvent.ADDRESS_PROPERTY_MAP_ADDED, null, mapName);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddressSetPropertyMap getAddressSetPropertyMap(String mapName) {
|
public AddressSetPropertyMap getAddressSetPropertyMap(String mapName) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.read()) {
|
||||||
try {
|
|
||||||
AddressSetPropertyMapDB map = addrSetPropertyMap.get(mapName);
|
AddressSetPropertyMapDB map = addrSetPropertyMap.get(mapName);
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
return map;
|
return map;
|
||||||
@@ -2309,15 +2266,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteAddressSetPropertyMap(String mapName) {
|
public void deleteAddressSetPropertyMap(String mapName) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
AddressSetPropertyMapDB pm = addrSetPropertyMap.remove(mapName);
|
AddressSetPropertyMapDB pm = addrSetPropertyMap.remove(mapName);
|
||||||
if (pm == null) {
|
if (pm == null) {
|
||||||
pm = AddressSetPropertyMapDB.getPropertyMap(this, mapName, this, addrMap, lock);
|
pm = AddressSetPropertyMapDB.getPropertyMap(this, mapName, this, addrMap, lock);
|
||||||
@@ -2327,29 +2280,21 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
setChanged(ProgramEvent.ADDRESS_PROPERTY_MAP_REMOVED, null, mapName);
|
setChanged(ProgramEvent.ADDRESS_PROPERTY_MAP_REMOVED, null, mapName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IntRangeMapDB createIntRangeMap(String mapName) throws DuplicateNameException {
|
public IntRangeMapDB createIntRangeMap(String mapName) throws DuplicateNameException {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
IntRangeMapDB map = IntRangeMapDB.createPropertyMap(this, mapName, this, addrMap, lock);
|
IntRangeMapDB map = IntRangeMapDB.createPropertyMap(this, mapName, this, addrMap, lock);
|
||||||
intRangePropertyMap.put(mapName, map);
|
intRangePropertyMap.put(mapName, map);
|
||||||
setChanged(ProgramEvent.INT_PROPERTY_MAP_ADDED, null, mapName);
|
setChanged(ProgramEvent.INT_PROPERTY_MAP_ADDED, null, mapName);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IntRangeMap getIntRangeMap(String mapName) {
|
public IntRangeMap getIntRangeMap(String mapName) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
IntRangeMapDB rangeMap = intRangePropertyMap.get(mapName);
|
IntRangeMapDB rangeMap = intRangePropertyMap.get(mapName);
|
||||||
if (rangeMap != null) {
|
if (rangeMap != null) {
|
||||||
return rangeMap;
|
return rangeMap;
|
||||||
@@ -2361,15 +2306,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
}
|
}
|
||||||
return rangeMap;
|
return rangeMap;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteIntRangeMap(String mapName) {
|
public void deleteIntRangeMap(String mapName) {
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
IntRangeMapDB rangeMap = intRangePropertyMap.remove(mapName);
|
IntRangeMapDB rangeMap = intRangePropertyMap.remove(mapName);
|
||||||
if (rangeMap == null) {
|
if (rangeMap == null) {
|
||||||
rangeMap = IntRangeMapDB.getPropertyMap(this, mapName, this, addrMap, lock);
|
rangeMap = IntRangeMapDB.getPropertyMap(this, mapName, this, addrMap, lock);
|
||||||
@@ -2380,10 +2321,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
setChanged(ProgramEvent.INT_PROPERTY_MAP_REMOVED, null, mapName);
|
setChanged(ProgramEvent.INT_PROPERTY_MAP_REMOVED, null, mapName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2518,15 +2455,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||||||
if (!(compilerSpec instanceof ProgramCompilerSpec)) {
|
if (!(compilerSpec instanceof ProgramCompilerSpec)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lock.acquire();
|
try (Closeable c = lock.write()) {
|
||||||
try {
|
|
||||||
((ProgramCompilerSpec) compilerSpec).installExtensions();
|
((ProgramCompilerSpec) compilerSpec).installExtensions();
|
||||||
getFunctionManager().invalidateCache(true);
|
getFunctionManager().invalidateCache(true);
|
||||||
getDataTypeManager().invalidateCache();
|
getDataTypeManager().invalidateCache();
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
lock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCompilerSpecOptions() {
|
private void registerCompilerSpecOptions() {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user