mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-571: Additional fixes for schemas and dbgeng
This commit is contained in:
@@ -73,7 +73,9 @@ public abstract class AbstractTargetObject<P extends TargetObject>
|
||||
this.typeHint = typeHint;
|
||||
|
||||
this.schema = schema;
|
||||
schema.validateTypeAndInterfaces(getProxy(), null, enforcesStrictSchema());
|
||||
if (schema != null) {
|
||||
schema.validateTypeAndInterfaces(getProxy(), null, enforcesStrictSchema());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,8 +126,12 @@ public abstract class AbstractTargetObject<P extends TargetObject>
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("<%s: path=%s model=%s schema=%s>",
|
||||
getClass().getSimpleName(), path, getModel(), schema.getName());
|
||||
if (schema == null) {
|
||||
return String.format("<%s: path=%s model=%s schema=NULL>", getClass().getSimpleName(),
|
||||
path, getModel());
|
||||
}
|
||||
return String.format("<%s: path=%s model=%s schema=%s>", getClass().getSimpleName(), path,
|
||||
getModel(), schema.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -237,7 +237,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
synchronized (this.elements) {
|
||||
delta = Delta.computeAndSet(this.elements, elements, Delta.SAME);
|
||||
}
|
||||
getSchema().validateElementDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
TargetObjectSchema schemax = getSchema();
|
||||
if (schemax != null) {
|
||||
schemax.validateElementDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
}
|
||||
doInvalidateElements(delta.removed.values(), reason);
|
||||
if (!delta.isEmpty()) {
|
||||
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||
@@ -279,7 +282,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
synchronized (elements) {
|
||||
delta = Delta.apply(this.elements, remove, add, Delta.SAME);
|
||||
}
|
||||
getSchema().validateElementDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
TargetObjectSchema schemax = getSchema();
|
||||
if (schemax != null) {
|
||||
schemax.validateElementDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
}
|
||||
doInvalidateElements(delta.removed.values(), reason);
|
||||
if (!delta.isEmpty()) {
|
||||
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||
@@ -395,7 +401,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
synchronized (this.attributes) {
|
||||
delta = Delta.computeAndSet(this.attributes, attributes, Delta.EQUAL);
|
||||
}
|
||||
getSchema().validateAttributeDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
TargetObjectSchema schemax = getSchema();
|
||||
if (schemax != null) {
|
||||
schemax.validateAttributeDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
}
|
||||
doInvalidateAttributes(delta.removed, reason);
|
||||
if (!delta.isEmpty()) {
|
||||
listeners.fire.attributesChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||
@@ -437,7 +446,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||
synchronized (attributes) {
|
||||
delta = Delta.apply(this.attributes, remove, add, Delta.EQUAL);
|
||||
}
|
||||
getSchema().validateAttributeDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
TargetObjectSchema schemax = getSchema();
|
||||
if (schemax != null) {
|
||||
schemax.validateAttributeDelta(getProxy(), delta, enforcesStrictSchema());
|
||||
}
|
||||
doInvalidateAttributes(delta.removed, reason);
|
||||
if (!delta.isEmpty()) {
|
||||
listeners.fire.attributesChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||
|
||||
@@ -27,7 +27,6 @@ import ghidra.dbg.attributes.TypedTargetObjectRef;
|
||||
import ghidra.dbg.error.DebuggerModelTypeException;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.AttributeSchema;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator;
|
||||
import ghidra.dbg.util.ValueUtils;
|
||||
@@ -161,44 +160,18 @@ import ghidra.util.Msg;
|
||||
*/
|
||||
public interface TargetObject extends TargetObjectRef {
|
||||
|
||||
Set<Class<? extends TargetObject>> ALL_INTERFACES = Set.of(
|
||||
TargetAccessConditioned.class,
|
||||
TargetAggregate.class,
|
||||
TargetAttachable.class,
|
||||
TargetAttacher.class,
|
||||
TargetBreakpointContainer.class,
|
||||
TargetBreakpointSpec.class,
|
||||
TargetDataTypeMember.class,
|
||||
TargetDataTypeNamespace.class,
|
||||
TargetDeletable.class,
|
||||
TargetDetachable.class,
|
||||
TargetBreakpointLocation.class,
|
||||
TargetEnvironment.class,
|
||||
TargetEventScope.class,
|
||||
TargetExecutionStateful.class,
|
||||
TargetFocusScope.class,
|
||||
TargetInterpreter.class,
|
||||
TargetInterruptible.class,
|
||||
TargetKillable.class,
|
||||
TargetLauncher.class,
|
||||
TargetMethod.class,
|
||||
TargetMemory.class,
|
||||
TargetMemoryRegion.class,
|
||||
TargetModule.class,
|
||||
TargetModuleContainer.class,
|
||||
TargetNamedDataType.class,
|
||||
TargetProcess.class,
|
||||
TargetRegister.class,
|
||||
TargetRegisterBank.class,
|
||||
TargetRegisterContainer.class,
|
||||
TargetResumable.class,
|
||||
TargetSection.class,
|
||||
TargetStack.class,
|
||||
TargetStackFrame.class,
|
||||
TargetSteppable.class,
|
||||
TargetSymbol.class,
|
||||
TargetSymbolNamespace.class,
|
||||
TargetThread.class);
|
||||
Set<Class<? extends TargetObject>> ALL_INTERFACES = Set.of(TargetAccessConditioned.class,
|
||||
TargetAggregate.class, TargetAttachable.class, TargetAttacher.class,
|
||||
TargetBreakpointContainer.class, TargetBreakpointSpec.class, TargetDataTypeMember.class,
|
||||
TargetDataTypeNamespace.class, TargetDeletable.class, TargetDetachable.class,
|
||||
TargetBreakpointLocation.class, TargetEnvironment.class, TargetEventScope.class,
|
||||
TargetExecutionStateful.class, TargetFocusScope.class, TargetInterpreter.class,
|
||||
TargetInterruptible.class, TargetKillable.class, TargetLauncher.class, TargetMethod.class,
|
||||
TargetMemory.class, TargetMemoryRegion.class, TargetModule.class,
|
||||
TargetModuleContainer.class, TargetNamedDataType.class, TargetProcess.class,
|
||||
TargetRegister.class, TargetRegisterBank.class, TargetRegisterContainer.class,
|
||||
TargetResumable.class, TargetSection.class, TargetStack.class, TargetStackFrame.class,
|
||||
TargetSteppable.class, TargetSymbol.class, TargetSymbolNamespace.class, TargetThread.class);
|
||||
Map<String, Class<? extends TargetObject>> INTERFACES_BY_NAME = initInterfacesByName();
|
||||
|
||||
/**
|
||||
@@ -209,12 +182,11 @@ public interface TargetObject extends TargetObjectRef {
|
||||
@Internal
|
||||
static Map<String, Class<? extends TargetObject>> initInterfacesByName() {
|
||||
return ALL_INTERFACES.stream()
|
||||
.collect(Collectors.toUnmodifiableMap(
|
||||
DebuggerObjectModel::requireIfaceName, i -> i));
|
||||
.collect(
|
||||
Collectors.toUnmodifiableMap(DebuggerObjectModel::requireIfaceName, i -> i));
|
||||
}
|
||||
|
||||
static List<Class<? extends TargetObject>> getInterfacesByName(
|
||||
Collection<String> names) {
|
||||
static List<Class<? extends TargetObject>> getInterfacesByName(Collection<String> names) {
|
||||
return names.stream()
|
||||
.filter(INTERFACES_BY_NAME::containsKey)
|
||||
.map(INTERFACES_BY_NAME::get)
|
||||
@@ -627,9 +599,10 @@ public interface TargetObject extends TargetObjectRef {
|
||||
* @return the value casted to the expected type, or the fallback value
|
||||
*/
|
||||
public default <T> T getTypedAttributeNowByName(String name, Class<T> cls, T fallback) {
|
||||
AttributeSchema as = getSchema().getAttributeSchema(name);
|
||||
Object obj = getCachedAttribute(name);
|
||||
return ValueUtils.expectType(obj, cls, this, name, fallback, as.isRequired());
|
||||
TargetObjectSchema schema = getSchema();
|
||||
boolean required = schema == null ? false : schema.getAttributeSchema(name).isRequired();
|
||||
return ValueUtils.expectType(obj, cls, this, name, fallback, required);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,27 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||
|
||||
public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
|
||||
public static class AnnotatedAttributeSchema extends DefaultAttributeSchema {
|
||||
protected final Class<?> javaClass;
|
||||
|
||||
public AnnotatedAttributeSchema(String name, SchemaName schema, boolean isRequired,
|
||||
boolean isFixed, boolean isHidden, Class<?> javaClass) {
|
||||
super(name, schema, isRequired, isFixed, isHidden);
|
||||
this.javaClass = javaClass;
|
||||
}
|
||||
|
||||
public AnnotatedAttributeSchema lower(AnnotatedAttributeSchema that) {
|
||||
if (this.javaClass.isAssignableFrom(that.javaClass)) {
|
||||
return that;
|
||||
}
|
||||
if (that.javaClass.isAssignableFrom(this.javaClass)) {
|
||||
return this;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot find lower of " + this.javaClass + " and " +
|
||||
that.javaClass + ". They are unrelated.");
|
||||
}
|
||||
}
|
||||
|
||||
static <T> Stream<Class<? extends T>> filterBounds(Class<T> base, Stream<Class<?>> bounds) {
|
||||
return bounds.filter(base::isAssignableFrom).map(c -> c.asSubclass(base));
|
||||
}
|
||||
@@ -129,8 +150,12 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
|
||||
if (info == null) {
|
||||
// TODO: Compile-time validation?
|
||||
Msg.warn(this, "Class " + cls + " is not annotated with @" +
|
||||
TargetObjectSchemaInfo.class.getSimpleName());
|
||||
DebuggerTargetObjectIface iface =
|
||||
cls.getAnnotation(DebuggerTargetObjectIface.class);
|
||||
if (iface == null) {
|
||||
Msg.warn(this, "Class " + cls + " is not annotated with @" +
|
||||
TargetObjectSchemaInfo.class.getSimpleName());
|
||||
}
|
||||
return EnumerableTargetObjectSchema.OBJECT.getName();
|
||||
}
|
||||
return namesByClass.computeIfAbsent(cls, c -> {
|
||||
@@ -235,8 +260,12 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
}
|
||||
}
|
||||
for (TargetAttributeType at : info.attributes()) {
|
||||
AttributeSchema attrSchema = attributeSchemaFromAnnotation(at);
|
||||
builder.addAttributeSchema(attrSchema, at);
|
||||
AnnotatedAttributeSchema attrSchema = attributeSchemaFromAnnotation(at);
|
||||
AttributeSchema exists = builder.getAttributeSchema(attrSchema.getName());
|
||||
if (exists != null) {
|
||||
attrSchema = attrSchema.lower((AnnotatedAttributeSchema) exists);
|
||||
}
|
||||
builder.replaceAttributeSchema(attrSchema, at);
|
||||
}
|
||||
|
||||
return builder.buildAndAdd();
|
||||
@@ -255,9 +284,9 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
protected AttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
|
||||
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
|
||||
at.fixed(), at.hidden());
|
||||
protected AnnotatedAttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
|
||||
return new AnnotatedAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
|
||||
at.fixed(), at.hidden(), at.type());
|
||||
}
|
||||
|
||||
protected AttributeSchema attributeSchemaFromAnnotatedMethod(Class<? extends TargetObject> cls,
|
||||
@@ -275,8 +304,8 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
}
|
||||
SchemaName primitiveName = EnumerableTargetObjectSchema.nameForPrimitive(ret);
|
||||
if (primitiveName != null) {
|
||||
return new DefaultAttributeSchema(name, primitiveName, at.required(), at.fixed(),
|
||||
at.hidden());
|
||||
return new AnnotatedAttributeSchema(name, primitiveName, at.required(), at.fixed(),
|
||||
at.hidden(), ret);
|
||||
}
|
||||
Set<Class<? extends TargetObject>> bounds = getBoundsOfObjectAttributeGetter(cls, method);
|
||||
if (bounds.size() != 1) {
|
||||
@@ -284,8 +313,9 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not identify unique attribute class for method " + method + ": " + bounds);
|
||||
}
|
||||
return new DefaultAttributeSchema(name, nameFromClass(bounds.iterator().next()),
|
||||
at.required(), at.fixed(), at.hidden());
|
||||
Class<? extends TargetObject> bound = bounds.iterator().next();
|
||||
return new AnnotatedAttributeSchema(name, nameFromClass(bound),
|
||||
at.required(), at.fixed(), at.hidden(), bound);
|
||||
}
|
||||
|
||||
protected SchemaName nameFromClass(Class<?> cls) {
|
||||
|
||||
@@ -148,6 +148,19 @@ public class SchemaBuilder {
|
||||
return Map.copyOf(attributeSchemas);
|
||||
}
|
||||
|
||||
public AttributeSchema getAttributeSchema(String name) {
|
||||
return attributeSchemas.get(name);
|
||||
}
|
||||
|
||||
public SchemaBuilder replaceAttributeSchema(AttributeSchema schema, Object origin) {
|
||||
if (schema.getName().equals("")) {
|
||||
return setDefaultAttributeSchema(schema);
|
||||
}
|
||||
attributeSchemas.put(schema.getName(), schema);
|
||||
attributeOrigins.put(schema.getName(), origin);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SchemaBuilder setDefaultAttributeSchema(AttributeSchema defaultAttributeSchema) {
|
||||
this.defaultAttributeSchema = defaultAttributeSchema;
|
||||
return this;
|
||||
|
||||
Reference in New Issue
Block a user