mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-571: Additional fixes for schemas and dbgeng
This commit is contained in:
parent
c81a17405d
commit
8fa549119d
18 changed files with 166 additions and 69 deletions
|
@ -91,6 +91,11 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||||
dbg.terminate();
|
dbg.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetObjectSchema getRootSchema() {
|
||||||
|
return root.getSchema();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<? extends TargetObject> fetchModelRoot() {
|
public CompletableFuture<? extends TargetObject> fetchModelRoot() {
|
||||||
return completedRoot;
|
return completedRoot;
|
||||||
|
|
|
@ -21,13 +21,19 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer;
|
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointSpec;
|
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointSpec;
|
||||||
import ghidra.dbg.target.TargetObject;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.schema.TargetAttributeType;
|
import ghidra.dbg.target.schema.TargetAttributeType;
|
||||||
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
|
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.util.datastruct.ListenerSet;
|
import ghidra.util.datastruct.ListenerSet;
|
||||||
|
|
||||||
@TargetObjectSchemaInfo(name = "BreakpointSpec", attributes = { //
|
@TargetObjectSchemaInfo(name = "BreakpointSpec", attributes = { //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetBreakpointSpec.CONTAINER_ATTRIBUTE_NAME, //
|
||||||
|
type = DbgModelTargetBreakpointContainerImpl.class), //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetBreakpointLocation.SPEC_ATTRIBUTE_NAME, //
|
||||||
|
type = DbgModelTargetBreakpointSpecImpl.class), //
|
||||||
@TargetAttributeType(type = Void.class) //
|
@TargetAttributeType(type = Void.class) //
|
||||||
}, canonicalContainer = true)
|
}, canonicalContainer = true)
|
||||||
public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class DbgModelTargetConnectorContainerImpl extends DbgModelTargetObjectIm
|
||||||
protected final DbgModelTargetKernelConnectorImpl kernelAttacher;
|
protected final DbgModelTargetKernelConnectorImpl kernelAttacher;
|
||||||
|
|
||||||
public DbgModelTargetConnectorContainerImpl(DbgModelTargetRoot root) {
|
public DbgModelTargetConnectorContainerImpl(DbgModelTargetRoot root) {
|
||||||
super(root.getModel(), root, "Connectors", "ConnectorsContainer");
|
super(root.getModel(), root, "Connectors", "ConnectorsContainer", null);
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
|
||||||
this.processLauncher = new DbgModelTargetProcessLaunchConnectorImpl(this, "Launch process");
|
this.processLauncher = new DbgModelTargetProcessLaunchConnectorImpl(this, "Launch process");
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Map;
|
||||||
import agent.dbgeng.manager.DbgModuleMemory;
|
import agent.dbgeng.manager.DbgModuleMemory;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetMemoryContainer;
|
import agent.dbgeng.model.iface2.DbgModelTargetMemoryContainer;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetMemoryRegion;
|
import agent.dbgeng.model.iface2.DbgModelTargetMemoryRegion;
|
||||||
|
import ghidra.dbg.target.TargetMemoryRegion;
|
||||||
import ghidra.dbg.target.schema.*;
|
import ghidra.dbg.target.schema.*;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -28,6 +29,9 @@ import ghidra.program.model.address.*;
|
||||||
@TargetObjectSchemaInfo(name = "MemoryRegion", elements = { //
|
@TargetObjectSchemaInfo(name = "MemoryRegion", elements = { //
|
||||||
@TargetElementType(type = Void.class) //
|
@TargetElementType(type = Void.class) //
|
||||||
}, attributes = { //
|
}, attributes = { //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, //
|
||||||
|
type = DbgModelTargetMemoryContainerImpl.class), //
|
||||||
@TargetAttributeType(name = "BaseAddress", type = Address.class), //
|
@TargetAttributeType(name = "BaseAddress", type = Address.class), //
|
||||||
@TargetAttributeType(name = "EndAddress", type = Address.class), //
|
@TargetAttributeType(name = "EndAddress", type = Address.class), //
|
||||||
@TargetAttributeType(name = "RegionSize", type = String.class), //
|
@TargetAttributeType(name = "RegionSize", type = String.class), //
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||||
|
|
||||||
public DbgModelTargetObjectImpl(AbstractDbgModel impl, TargetObject parent, String name,
|
public DbgModelTargetObjectImpl(AbstractDbgModel impl, TargetObject parent, String name,
|
||||||
String typeHint) {
|
String typeHint) {
|
||||||
super(impl, parent, name, typeHint);
|
super(impl, parent, name, typeHint, null);
|
||||||
getManager().addStateListener(accessListener);
|
getManager().addStateListener(accessListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,9 @@ import ghidra.dbg.util.ConversionUtils;
|
||||||
@TargetObjectSchemaInfo(name = "RegisterContainer", elements = { //
|
@TargetObjectSchemaInfo(name = "RegisterContainer", elements = { //
|
||||||
@TargetElementType(type = DbgModelTargetRegisterImpl.class) //
|
@TargetElementType(type = DbgModelTargetRegisterImpl.class) //
|
||||||
}, attributes = { //
|
}, attributes = { //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, //
|
||||||
|
type=DbgModelTargetRegisterContainerImpl.class),
|
||||||
@TargetAttributeType(type = Void.class) //
|
@TargetAttributeType(type = Void.class) //
|
||||||
}, canonicalContainer = true)
|
}, canonicalContainer = true)
|
||||||
public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl
|
public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl
|
||||||
|
|
|
@ -21,12 +21,16 @@ import java.util.Map;
|
||||||
import agent.dbgeng.manager.impl.DbgRegister;
|
import agent.dbgeng.manager.impl.DbgRegister;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetRegister;
|
import agent.dbgeng.model.iface2.DbgModelTargetRegister;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetRegisterContainerAndBank;
|
import agent.dbgeng.model.iface2.DbgModelTargetRegisterContainerAndBank;
|
||||||
|
import ghidra.dbg.target.TargetRegister;
|
||||||
import ghidra.dbg.target.schema.*;
|
import ghidra.dbg.target.schema.*;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
|
|
||||||
@TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = { //
|
@TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = { //
|
||||||
@TargetElementType(type = Void.class) //
|
@TargetElementType(type = Void.class) //
|
||||||
}, attributes = { //
|
}, attributes = { //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetRegister.CONTAINER_ATTRIBUTE_NAME, //
|
||||||
|
type = DbgModelTargetRegisterContainerImpl.class), //
|
||||||
@TargetAttributeType(type = Void.class) //
|
@TargetAttributeType(type = Void.class) //
|
||||||
})
|
})
|
||||||
public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
|
public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import agent.dbgeng.manager.impl.DbgMinimalSymbol;
|
import agent.dbgeng.manager.impl.DbgMinimalSymbol;
|
||||||
import agent.dbgeng.model.iface2.DbgModelTargetSymbol;
|
import agent.dbgeng.model.iface2.DbgModelTargetSymbol;
|
||||||
|
import ghidra.dbg.target.TargetSymbol;
|
||||||
import ghidra.dbg.target.schema.*;
|
import ghidra.dbg.target.schema.*;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
@ -27,6 +28,9 @@ import ghidra.program.model.address.Address;
|
||||||
@TargetObjectSchemaInfo(name = "Symbol", elements = { //
|
@TargetObjectSchemaInfo(name = "Symbol", elements = { //
|
||||||
@TargetElementType(type = Void.class) //
|
@TargetElementType(type = Void.class) //
|
||||||
}, attributes = { //
|
}, attributes = { //
|
||||||
|
@TargetAttributeType( //
|
||||||
|
name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, //
|
||||||
|
type = DbgModelTargetSymbolContainerImpl.class), //
|
||||||
@TargetAttributeType(type = Void.class) //
|
@TargetAttributeType(type = Void.class) //
|
||||||
})
|
})
|
||||||
public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
|
public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
|
||||||
|
@ -52,6 +56,7 @@ public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
|
||||||
|
|
||||||
changeAttributes(List.of(), List.of(), Map.of( //
|
changeAttributes(List.of(), List.of(), Map.of( //
|
||||||
// TODO: DATA_TYPE
|
// TODO: DATA_TYPE
|
||||||
|
NAMESPACE_ATTRIBUTE_NAME, symbols, //
|
||||||
VALUE_ATTRIBUTE_NAME, value, //
|
VALUE_ATTRIBUTE_NAME, value, //
|
||||||
SIZE_ATTRIBUTE_NAME, size, //
|
SIZE_ATTRIBUTE_NAME, size, //
|
||||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package agent.dbgmodel.model.impl;
|
package agent.dbgmodel.model.impl;
|
||||||
|
|
||||||
import ghidra.dbg.target.TargetAggregate;
|
import ghidra.dbg.target.TargetAggregate;
|
||||||
|
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||||
|
|
||||||
public class DbgModel2DefaultTargetModelRoot extends DbgModel2TargetObjectImpl
|
public class DbgModel2DefaultTargetModelRoot extends DbgModel2TargetObjectImpl
|
||||||
implements TargetAggregate {
|
implements TargetAggregate {
|
||||||
|
@ -23,4 +24,9 @@ public class DbgModel2DefaultTargetModelRoot extends DbgModel2TargetObjectImpl
|
||||||
public DbgModel2DefaultTargetModelRoot(DbgModel2Impl model, String typeHint) {
|
public DbgModel2DefaultTargetModelRoot(DbgModel2Impl model, String typeHint) {
|
||||||
super(model, null, null, typeHint);
|
super(model, null, null, typeHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DbgModel2DefaultTargetModelRoot(DbgModel2Impl model, String typeHint,
|
||||||
|
TargetObjectSchema schema) {
|
||||||
|
super(model, null, null, typeHint, schema);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class DbgModel2Impl extends AbstractDbgModel {
|
||||||
|
|
||||||
public DbgModel2Impl() {
|
public DbgModel2Impl() {
|
||||||
this.dbg = new DbgManager2Impl();
|
this.dbg = new DbgManager2Impl();
|
||||||
this.root = new DbgModel2TargetRootImpl(this);
|
this.root = new DbgModel2TargetRootImpl(this, null);
|
||||||
this.completedRoot = CompletableFuture.completedFuture(root);
|
this.completedRoot = CompletableFuture.completedFuture(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility;
|
||||||
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet;
|
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet;
|
||||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||||
|
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator;
|
import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
@ -72,7 +73,12 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||||
|
|
||||||
public DbgModel2TargetObjectImpl(AbstractDbgModel model, TargetObject parent, String name,
|
public DbgModel2TargetObjectImpl(AbstractDbgModel model, TargetObject parent, String name,
|
||||||
String typeHint) {
|
String typeHint) {
|
||||||
super(model, parent, name, typeHint);
|
super(model, parent, name, typeHint, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbgModel2TargetObjectImpl(AbstractDbgModel model, TargetObject parent, String name,
|
||||||
|
String typeHint, TargetObjectSchema schema) {
|
||||||
|
super(model, parent, name, typeHint, schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ghidra.async.TypeSpec;
|
||||||
import ghidra.dbg.target.*;
|
import ghidra.dbg.target.*;
|
||||||
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointListener;
|
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointListener;
|
||||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||||
|
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
@ -67,6 +68,29 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
|
||||||
impl.getManager().addEventsListener(this);
|
impl.getManager().addEventsListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DbgModel2TargetRootImpl(DbgModel2Impl impl, TargetObjectSchema schema) {
|
||||||
|
super(impl, "Debugger", schema);
|
||||||
|
this.impl = impl;
|
||||||
|
|
||||||
|
this.available = new DbgModel2TargetAvailableContainerImpl(this);
|
||||||
|
this.connectors = new DbgModelTargetConnectorContainerImpl(this);
|
||||||
|
this.systemMarker = new DbgModel2TargetSystemMarkerImpl(this);
|
||||||
|
|
||||||
|
DbgModelTargetConnector defaultConnector = connectors.getDefaultConnector();
|
||||||
|
changeAttributes(List.of(), List.of( //
|
||||||
|
available, //
|
||||||
|
connectors, //
|
||||||
|
systemMarker //
|
||||||
|
), Map.of( //
|
||||||
|
DISPLAY_ATTRIBUTE_NAME, "Debugger", //
|
||||||
|
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters() //
|
||||||
|
// ARCH_ATTRIBUTE_NAME, "x86_64", //
|
||||||
|
// DEBUGGER_ATTRIBUTE_NAME, "dbgeng", //
|
||||||
|
// OS_ATTRIBUTE_NAME, "Windows", //
|
||||||
|
), "Initialized");
|
||||||
|
impl.getManager().addEventsListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setFocus(DbgModelSelectableObject sel) {
|
public boolean setFocus(DbgModelSelectableObject sel) {
|
||||||
boolean doFire;
|
boolean doFire;
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class JdiModelTargetObjectImpl extends
|
||||||
private boolean modified;
|
private boolean modified;
|
||||||
|
|
||||||
public JdiModelTargetObjectImpl(JdiModelTargetObject parent, String id) {
|
public JdiModelTargetObjectImpl(JdiModelTargetObject parent, String id) {
|
||||||
super(parent.getModel(), parent, id, "Object");
|
super(parent.getModel(), parent, id, "Object", null);
|
||||||
this.impl = parent.getModelImpl();
|
this.impl = parent.getModelImpl();
|
||||||
this.mirror = (Mirror) parent.getObject();
|
this.mirror = (Mirror) parent.getObject();
|
||||||
this.object = null;
|
this.object = null;
|
||||||
|
|
|
@ -73,8 +73,10 @@ public abstract class AbstractTargetObject<P extends TargetObject>
|
||||||
this.typeHint = typeHint;
|
this.typeHint = typeHint;
|
||||||
|
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
|
if (schema != null) {
|
||||||
schema.validateTypeAndInterfaces(getProxy(), null, enforcesStrictSchema());
|
schema.validateTypeAndInterfaces(getProxy(), null, enforcesStrictSchema());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an alternative for {@code this} when invoking the listeners.
|
* Get an alternative for {@code this} when invoking the listeners.
|
||||||
|
@ -124,8 +126,12 @@ public abstract class AbstractTargetObject<P extends TargetObject>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("<%s: path=%s model=%s schema=%s>",
|
if (schema == null) {
|
||||||
getClass().getSimpleName(), path, getModel(), schema.getName());
|
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
|
@Override
|
||||||
|
|
|
@ -237,7 +237,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||||
synchronized (this.elements) {
|
synchronized (this.elements) {
|
||||||
delta = Delta.computeAndSet(this.elements, elements, Delta.SAME);
|
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);
|
doInvalidateElements(delta.removed.values(), reason);
|
||||||
if (!delta.isEmpty()) {
|
if (!delta.isEmpty()) {
|
||||||
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||||
|
@ -279,7 +282,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||||
synchronized (elements) {
|
synchronized (elements) {
|
||||||
delta = Delta.apply(this.elements, remove, add, Delta.SAME);
|
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);
|
doInvalidateElements(delta.removed.values(), reason);
|
||||||
if (!delta.isEmpty()) {
|
if (!delta.isEmpty()) {
|
||||||
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
listeners.fire.elementsChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||||
|
@ -395,7 +401,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||||
synchronized (this.attributes) {
|
synchronized (this.attributes) {
|
||||||
delta = Delta.computeAndSet(this.attributes, attributes, Delta.EQUAL);
|
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);
|
doInvalidateAttributes(delta.removed, reason);
|
||||||
if (!delta.isEmpty()) {
|
if (!delta.isEmpty()) {
|
||||||
listeners.fire.attributesChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
listeners.fire.attributesChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
||||||
|
@ -437,7 +446,10 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
|
||||||
synchronized (attributes) {
|
synchronized (attributes) {
|
||||||
delta = Delta.apply(this.attributes, remove, add, Delta.EQUAL);
|
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);
|
doInvalidateAttributes(delta.removed, reason);
|
||||||
if (!delta.isEmpty()) {
|
if (!delta.isEmpty()) {
|
||||||
listeners.fire.attributesChanged(getProxy(), delta.getKeysRemoved(), delta.added);
|
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.error.DebuggerModelTypeException;
|
||||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||||
import ghidra.dbg.target.schema.*;
|
import ghidra.dbg.target.schema.*;
|
||||||
import ghidra.dbg.target.schema.TargetObjectSchema.AttributeSchema;
|
|
||||||
import ghidra.dbg.util.PathUtils;
|
import ghidra.dbg.util.PathUtils;
|
||||||
import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator;
|
import ghidra.dbg.util.PathUtils.TargetObjectKeyComparator;
|
||||||
import ghidra.dbg.util.ValueUtils;
|
import ghidra.dbg.util.ValueUtils;
|
||||||
|
@ -161,44 +160,18 @@ import ghidra.util.Msg;
|
||||||
*/
|
*/
|
||||||
public interface TargetObject extends TargetObjectRef {
|
public interface TargetObject extends TargetObjectRef {
|
||||||
|
|
||||||
Set<Class<? extends TargetObject>> ALL_INTERFACES = Set.of(
|
Set<Class<? extends TargetObject>> ALL_INTERFACES = Set.of(TargetAccessConditioned.class,
|
||||||
TargetAccessConditioned.class,
|
TargetAggregate.class, TargetAttachable.class, TargetAttacher.class,
|
||||||
TargetAggregate.class,
|
TargetBreakpointContainer.class, TargetBreakpointSpec.class, TargetDataTypeMember.class,
|
||||||
TargetAttachable.class,
|
TargetDataTypeNamespace.class, TargetDeletable.class, TargetDetachable.class,
|
||||||
TargetAttacher.class,
|
TargetBreakpointLocation.class, TargetEnvironment.class, TargetEventScope.class,
|
||||||
TargetBreakpointContainer.class,
|
TargetExecutionStateful.class, TargetFocusScope.class, TargetInterpreter.class,
|
||||||
TargetBreakpointSpec.class,
|
TargetInterruptible.class, TargetKillable.class, TargetLauncher.class, TargetMethod.class,
|
||||||
TargetDataTypeMember.class,
|
TargetMemory.class, TargetMemoryRegion.class, TargetModule.class,
|
||||||
TargetDataTypeNamespace.class,
|
TargetModuleContainer.class, TargetNamedDataType.class, TargetProcess.class,
|
||||||
TargetDeletable.class,
|
TargetRegister.class, TargetRegisterBank.class, TargetRegisterContainer.class,
|
||||||
TargetDetachable.class,
|
TargetResumable.class, TargetSection.class, TargetStack.class, TargetStackFrame.class,
|
||||||
TargetBreakpointLocation.class,
|
TargetSteppable.class, TargetSymbol.class, TargetSymbolNamespace.class, TargetThread.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();
|
Map<String, Class<? extends TargetObject>> INTERFACES_BY_NAME = initInterfacesByName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,12 +182,11 @@ public interface TargetObject extends TargetObjectRef {
|
||||||
@Internal
|
@Internal
|
||||||
static Map<String, Class<? extends TargetObject>> initInterfacesByName() {
|
static Map<String, Class<? extends TargetObject>> initInterfacesByName() {
|
||||||
return ALL_INTERFACES.stream()
|
return ALL_INTERFACES.stream()
|
||||||
.collect(Collectors.toUnmodifiableMap(
|
.collect(
|
||||||
DebuggerObjectModel::requireIfaceName, i -> i));
|
Collectors.toUnmodifiableMap(DebuggerObjectModel::requireIfaceName, i -> i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Class<? extends TargetObject>> getInterfacesByName(
|
static List<Class<? extends TargetObject>> getInterfacesByName(Collection<String> names) {
|
||||||
Collection<String> names) {
|
|
||||||
return names.stream()
|
return names.stream()
|
||||||
.filter(INTERFACES_BY_NAME::containsKey)
|
.filter(INTERFACES_BY_NAME::containsKey)
|
||||||
.map(INTERFACES_BY_NAME::get)
|
.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
|
* @return the value casted to the expected type, or the fallback value
|
||||||
*/
|
*/
|
||||||
public default <T> T getTypedAttributeNowByName(String name, Class<T> cls, T fallback) {
|
public default <T> T getTypedAttributeNowByName(String name, Class<T> cls, T fallback) {
|
||||||
AttributeSchema as = getSchema().getAttributeSchema(name);
|
|
||||||
Object obj = getCachedAttribute(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 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) {
|
static <T> Stream<Class<? extends T>> filterBounds(Class<T> base, Stream<Class<?>> bounds) {
|
||||||
return bounds.filter(base::isAssignableFrom).map(c -> c.asSubclass(base));
|
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);
|
TargetObjectSchemaInfo info = cls.getAnnotation(TargetObjectSchemaInfo.class);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
// TODO: Compile-time validation?
|
// TODO: Compile-time validation?
|
||||||
|
DebuggerTargetObjectIface iface =
|
||||||
|
cls.getAnnotation(DebuggerTargetObjectIface.class);
|
||||||
|
if (iface == null) {
|
||||||
Msg.warn(this, "Class " + cls + " is not annotated with @" +
|
Msg.warn(this, "Class " + cls + " is not annotated with @" +
|
||||||
TargetObjectSchemaInfo.class.getSimpleName());
|
TargetObjectSchemaInfo.class.getSimpleName());
|
||||||
|
}
|
||||||
return EnumerableTargetObjectSchema.OBJECT.getName();
|
return EnumerableTargetObjectSchema.OBJECT.getName();
|
||||||
}
|
}
|
||||||
return namesByClass.computeIfAbsent(cls, c -> {
|
return namesByClass.computeIfAbsent(cls, c -> {
|
||||||
|
@ -235,8 +260,12 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (TargetAttributeType at : info.attributes()) {
|
for (TargetAttributeType at : info.attributes()) {
|
||||||
AttributeSchema attrSchema = attributeSchemaFromAnnotation(at);
|
AnnotatedAttributeSchema attrSchema = attributeSchemaFromAnnotation(at);
|
||||||
builder.addAttributeSchema(attrSchema, at);
|
AttributeSchema exists = builder.getAttributeSchema(attrSchema.getName());
|
||||||
|
if (exists != null) {
|
||||||
|
attrSchema = attrSchema.lower((AnnotatedAttributeSchema) exists);
|
||||||
|
}
|
||||||
|
builder.replaceAttributeSchema(attrSchema, at);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.buildAndAdd();
|
return builder.buildAndAdd();
|
||||||
|
@ -255,9 +284,9 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
|
protected AnnotatedAttributeSchema attributeSchemaFromAnnotation(TargetAttributeType at) {
|
||||||
return new DefaultAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
|
return new AnnotatedAttributeSchema(at.name(), nameFromClass(at.type()), at.required(),
|
||||||
at.fixed(), at.hidden());
|
at.fixed(), at.hidden(), at.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttributeSchema attributeSchemaFromAnnotatedMethod(Class<? extends TargetObject> cls,
|
protected AttributeSchema attributeSchemaFromAnnotatedMethod(Class<? extends TargetObject> cls,
|
||||||
|
@ -275,8 +304,8 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||||
}
|
}
|
||||||
SchemaName primitiveName = EnumerableTargetObjectSchema.nameForPrimitive(ret);
|
SchemaName primitiveName = EnumerableTargetObjectSchema.nameForPrimitive(ret);
|
||||||
if (primitiveName != null) {
|
if (primitiveName != null) {
|
||||||
return new DefaultAttributeSchema(name, primitiveName, at.required(), at.fixed(),
|
return new AnnotatedAttributeSchema(name, primitiveName, at.required(), at.fixed(),
|
||||||
at.hidden());
|
at.hidden(), ret);
|
||||||
}
|
}
|
||||||
Set<Class<? extends TargetObject>> bounds = getBoundsOfObjectAttributeGetter(cls, method);
|
Set<Class<? extends TargetObject>> bounds = getBoundsOfObjectAttributeGetter(cls, method);
|
||||||
if (bounds.size() != 1) {
|
if (bounds.size() != 1) {
|
||||||
|
@ -284,8 +313,9 @@ public class AnnotatedSchemaContext extends DefaultSchemaContext {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Could not identify unique attribute class for method " + method + ": " + bounds);
|
"Could not identify unique attribute class for method " + method + ": " + bounds);
|
||||||
}
|
}
|
||||||
return new DefaultAttributeSchema(name, nameFromClass(bounds.iterator().next()),
|
Class<? extends TargetObject> bound = bounds.iterator().next();
|
||||||
at.required(), at.fixed(), at.hidden());
|
return new AnnotatedAttributeSchema(name, nameFromClass(bound),
|
||||||
|
at.required(), at.fixed(), at.hidden(), bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SchemaName nameFromClass(Class<?> cls) {
|
protected SchemaName nameFromClass(Class<?> cls) {
|
||||||
|
|
|
@ -148,6 +148,19 @@ public class SchemaBuilder {
|
||||||
return Map.copyOf(attributeSchemas);
|
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) {
|
public SchemaBuilder setDefaultAttributeSchema(AttributeSchema defaultAttributeSchema) {
|
||||||
this.defaultAttributeSchema = defaultAttributeSchema;
|
this.defaultAttributeSchema = defaultAttributeSchema;
|
||||||
return this;
|
return this;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue