GP-1618 Improved DataType.getParents to avoid returning duplicates for DB case which is used during change notification processing.

This commit is contained in:
ghidra1 2021-12-22 15:12:50 -05:00
parent 948e4edeb1
commit 87cc46efa2
11 changed files with 91 additions and 47 deletions

View file

@ -17,8 +17,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Collection;
import db.DBRecord;
import ghidra.docking.settings.Settings;
@ -452,10 +451,8 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
}
@Override
public DataType[] getParents() {
List<DataType> parents = dataMgr.getParentDataTypes(key);
DataType[] array = new DataType[parents.size()];
return parents.toArray(array);
public Collection<DataType> getParents() {
return dataMgr.getParentDataTypes(key);
}
@Override

View file

@ -1350,8 +1350,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
private void replaceUsesInOtherDataTypes(DataType existingDt, DataType newDt) {
if (existingDt instanceof DataTypeDB) {
DataType[] dts = existingDt.getParents();
for (DataType dt : dts) {
for (DataType dt : existingDt.getParents()) {
dt.dataTypeReplaced(existingDt, newDt);
}
}
@ -3482,12 +3481,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
List<DataType> getParentDataTypes(long childID) {
List<DataType> getParentDataTypes(long dataTypeId) {
lock.acquire();
try {
long[] ids = parentChildAdapter.getParentIds(childID);
Set<Long> parentIds = parentChildAdapter.getParentIds(dataTypeId);
// NOTE: Use of Set for containing datatypes is avoided due to the excessive
// overhead of DataType.equals
List<DataType> dts = new ArrayList<>();
for (long id : ids) {
for (long id : parentIds) {
DataType dt = getDataType(id);
if (dt == null) {
// cleanup invalid records for missing parent
@ -3498,7 +3499,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
return dts;
}
catch (IOException e) {
dbError(e);
@ -3509,6 +3509,52 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return null;
}
/**
* NOTE: method use should be avoided since use of a Set for containing
* datatypes can cause use of DataType.equals method which should
* be avoided for performance reasons.
* @param dataTypeId id of datatype whose parents should be found
* @return set of known parent datatypes
*/
@Deprecated
Set<DataType> getParentDataTypeSet(long dataTypeId) {
lock.acquire();
try {
Set<Long> parentIds = parentChildAdapter.getParentIds(dataTypeId);
Set<DataType> dts = new HashSet<>();
for (long id : parentIds) {
DataType dt = getDataType(id);
if (dt == null) {
// cleanup invalid records for missing parent
attemptRecordRemovalForParent(id);
}
else {
dts.add(dt);
}
}
return dts;
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}
return null;
}
@Override
public Set<DataType> getDataTypesContaining(DataType dataType) {
if (dataType instanceof DataTypeDB) {
DataTypeDB dataTypeDb = (DataTypeDB) dataType;
if (dataTypeDb.getDataTypeManager() != this) {
return Set.of();
}
return getParentDataTypeSet(dataTypeDb.getKey());
}
return Set.of();
}
private void attemptRecordRemovalForParent(long parentKey) throws IOException {
lock.acquire();
try {
@ -3521,24 +3567,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
@Override
public Set<DataType> getDataTypesContaining(DataType dataType) {
Set<DataType> set = new HashSet<>();
if (dataType instanceof DataTypeDB) {
long dataTypeID = ((DataTypeDB) dataType).getKey();
try {
long[] ids = parentChildAdapter.getParentIds(dataTypeID);
for (long id : ids) {
set.add(getDataType(id));
}
}
catch (IOException e) {
dbError(e);
}
}
return set;
}
@Override
public Pointer getPointer(DataType dt) {
return new PointerDataType(dt, -1, this);

View file

@ -16,6 +16,7 @@
package ghidra.program.database.data;
import java.io.IOException;
import java.util.Set;
import db.DBConstants;
import db.DBHandle;
@ -71,7 +72,15 @@ abstract class ParentChildAdapter {
abstract void removeRecord(long parentID, long childID) throws IOException;
abstract long[] getParentIds(long childID) throws IOException;
/**
* Get the unique set of parent ID associated with the specified childID.
* Since composite parents may have duplicate parent-child records, this method
* avoids returning the same parent more than once.
* @param childID child datatype ID
* @return set of parent datatype IDs
* @throws IOException if a DB IO error occurs
*/
abstract Set<Long> getParentIds(long childID) throws IOException;
abstract void removeAllRecordsForParent(long parentID) throws IOException;

View file

@ -16,6 +16,7 @@
package ghidra.program.database.data;
import java.io.IOException;
import java.util.Set;
import db.DBHandle;
import ghidra.util.exception.VersionException;
@ -43,8 +44,8 @@ class ParentChildDBAdapterNoTable extends ParentChildAdapter {
}
@Override
long[] getParentIds(long childID) throws IOException {
return new long[0];
Set<Long> getParentIds(long childID) throws IOException {
return Set.of();
}
@Override

View file

@ -16,6 +16,8 @@
package ghidra.program.database.data;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import db.*;
import ghidra.util.exception.VersionException;
@ -73,12 +75,12 @@ class ParentChildDBAdapterV0 extends ParentChildAdapter {
}
@Override
long[] getParentIds(long childID) throws IOException {
Set<Long> getParentIds(long childID) throws IOException {
Field[] ids = table.findRecords(new LongField(childID), CHILD_COL);
long[] parentIds = new long[ids.length];
Set<Long> parentIds = new HashSet<>(ids.length);
for (int i = 0; i < ids.length; i++) {
DBRecord rec = table.getRecord(ids[i]);
parentIds[i] = rec.getLongValue(PARENT_COL);
parentIds.add(rec.getLongValue(PARENT_COL));
}
return parentIds;
}

View file

@ -16,6 +16,7 @@
package ghidra.program.model.data;
import java.net.URL;
import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.program.model.mem.MemBuffer;
@ -170,7 +171,7 @@ public abstract class AbstractDataType implements DataType {
}
@Override
public DataType[] getParents() {
public Collection<DataType> getParents() {
// not-applicable
return null;
}

View file

@ -16,6 +16,7 @@
package ghidra.program.model.data;
import java.net.URL;
import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
@ -484,11 +485,13 @@ public interface DataType {
public void dataTypeNameChanged(DataType dt, String oldName);
/**
* Get the parents of this datatype
*
* @return an array of parents of this datatype
* Get the parents of this datatype.
*
* NOTE: This method is intended to be used on a DB-managed datatype only and is not
* fully supported for use with non-DB datatype instances.
* @return parents of this datatype
*/
public DataType[] getParents();
public Collection<DataType> getParents();
/**
* Gets the alignment to be used when aligning this datatype within another datatype.

View file

@ -120,7 +120,7 @@ public abstract class DataTypeImpl extends AbstractDataType {
}
@Override
public DataType[] getParents() {
public Collection<DataType> getParents() {
List<DataType> parents = new ArrayList<>();
Iterator<WeakReference<DataType>> iterator = parentList.iterator();
while (iterator.hasNext()) {
@ -133,8 +133,7 @@ public abstract class DataTypeImpl extends AbstractDataType {
parents.add(dataType);
}
}
DataType[] array = new DataType[parents.size()];
return parents.toArray(array);
return parents;
}
/**

View file

@ -530,8 +530,12 @@ public interface DataTypeManager {
/**
* Returns the data types within this data type manager that contain the specified data type.
* The specified dataType must belong to this datatype manager. An empty set will be
* returned for unsupported datatype instances.
* @param dataType the data type
* @return a set of data types that contain the specified data type.
* @deprecated the method {@link DataType#getParents()} should be used instead.
* Use of {@link Set} implementations for containing DataTypes is also inefficient.
*/
public Set<DataType> getDataTypesContaining(DataType dataType);
}

View file

@ -17,6 +17,7 @@ package ghidra.program.model.data;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
@ -254,7 +255,7 @@ public class StubDataType implements DataType {
}
@Override
public DataType[] getParents() {
public Collection<DataType> getParents() {
throw new UnsupportedOperationException();
}