GP-0: Fixing test fallout from GP-690

This commit is contained in:
Dan
2021-02-16 12:15:23 -05:00
parent 3093e2dd2a
commit b69c3d6c50
16 changed files with 155 additions and 88 deletions
@@ -267,8 +267,8 @@ public class GadpClientServerTest {
return processes;
}
public void addLinks() {
assertNotNull(available.fetchElements().getNow(null));
public void addLinks() throws Throwable {
waitOn(available.fetchElements());
links.setElements(List.of(), Map.of(
"1", available.getCachedElements().get("2"),
"2", available.getCachedElements().get("1")),
@@ -309,7 +309,7 @@ public class GadpClientServerTest {
}
Object ret = method.testInvoke(invocation.getValue());
changeAttributes(List.of(), Map.of(name, ret), "Invoked " + name);
return CompletableFuture.completedFuture(ret);
return CompletableFuture.completedFuture(ret).thenCompose(model::gateFuture);
}
@Override
@@ -338,8 +338,7 @@ public class GadpClientServerTest {
implements TargetMethod<TestTargetMethod> {
private Function<String, ?> method;
public TestTargetMethod(TargetObject parent, String key,
Function<String, ?> method) {
public TestTargetMethod(TargetObject parent, String key, Function<String, ?> method) {
super(parent.getModel(), parent, key, "Method");
this.method = method;
@@ -401,8 +400,7 @@ public class GadpClientServerTest {
}
@Override
public CompletableFuture<Void> requestElements(
boolean refresh) {
public CompletableFuture<Void> requestElements(boolean refresh) {
setElements(List.of(
new TestGadpTargetAvailable(this, 1, "echo"),
new TestGadpTargetAvailable(this, 2, "dd")),
@@ -619,7 +617,7 @@ public class GadpClientServerTest {
TargetMethod<?> method = waitOn(methodRef.as(TargetMethod.tclass).fetch());
assertNotNull(method);
assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)")));
runner.server.model.session.available.punct = '?';
runner.server.model.session.available.punct = '?'; // No effect before flush
assertEquals("Hello, World!", waitOn(avail.fetchAttribute("greet(World)")));
// Flush the cache
@@ -746,6 +744,7 @@ public class GadpClientServerTest {
(TargetObjectRef) attrs.get(TargetFocusScope.FOCUS_ATTRIBUTE_NAME);
// NOTE: Could be actual object, but need not be
assertEquals(List.of("Processes", "[0]"), ref.getPath());
waitOn(focusPath);
waitOn(client.close());
assertFalse(failed.get());
@@ -60,18 +60,17 @@ import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.datastruct.CollectionChangeListener;
import ghidra.util.datastruct.ListenerSet;
@PluginInfo( //
shortDescription = "Debugger models manager service", //
description = "Manage debug sessions, connections, and trace recording", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.HIDDEN, //
servicesRequired = { //
}, //
servicesProvided = { //
DebuggerModelService.class, //
} //
)
@PluginInfo(
shortDescription = "Debugger models manager service",
description = "Manage debug sessions, connections, and trace recording",
category = PluginCategoryNames.DEBUGGER,
packageName = DebuggerPluginPackage.NAME,
status = PluginStatus.HIDDEN,
servicesRequired = {
},
servicesProvided = {
DebuggerModelService.class,
})
public class DebuggerModelServicePlugin extends Plugin
implements DebuggerModelServiceInternal, FrontEndOnly {
@@ -125,6 +124,9 @@ public class DebuggerModelServicePlugin extends Plugin
this.root = r;
}
r.addListener(this.forRemoval);
if (!r.isValid()) {
forRemoval.invalidated(root, "Who knows?");
}
CompletableFuture<? extends TargetFocusScope<?>> findSuitable =
DebugModelConventions.findSuitable(TargetFocusScope.tclass, r);
return findSuitable;
@@ -132,7 +134,9 @@ public class DebuggerModelServicePlugin extends Plugin
synchronized (this) {
this.focusScope = fs;
}
fs.addListener(this.forFocus);
if (fs != null) {
fs.addListener(this.forFocus);
}
});
}
@@ -436,6 +440,10 @@ public class DebuggerModelServicePlugin extends Plugin
@Override
public synchronized DebuggerObjectModel getCurrentModel() {
if (!currentModel.isAlive()) {
currentModel = null;
}
return currentModel;
}
@@ -24,6 +24,20 @@ import ghidra.program.model.lang.Language;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
/**
* An executor which can perform (some of) its work asynchronously
*
* <p>
* Note that a future returned from, e.g., {@link #executeAsync(SleighProgram, SleighUseropLibrary)}
* may complete before the computation has actually been performed. They complete when all of the
* operations have been scheduled, and the last future has been written into the state. (This
* typically happens when any branch conditions have completed). Instead, a caller should read from
* the async state, which will return a future. The state will ensure that future does not complete
* until the computation has been performed -- assuming the requested variable actually depends on
* that computation.
*
* @param <T> the type of values in the state
*/
public class AsyncPcodeExecutor<T> extends PcodeExecutor<CompletableFuture<T>> {
public AsyncPcodeExecutor(Language language, PcodeArithmetic<CompletableFuture<T>> behavior,
PcodeExecutorStatePiece<CompletableFuture<T>, CompletableFuture<T>> state) {
@@ -52,8 +52,7 @@ public class AsyncWrappedPcodeExecutorStatePiece<A, T>
}
protected CompletableFuture<T> doGetVar(AddressSpace space, CompletableFuture<A> offset,
int size,
boolean truncateAddressableUnit) {
int size, boolean truncateAddressableUnit) {
return offset.thenApply(off -> {
return state.getVar(space, off, size, truncateAddressableUnit);
});
@@ -35,6 +35,7 @@ import ghidra.dbg.model.TestTargetMemoryRegion;
import ghidra.dbg.model.TestTargetProcess;
import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.util.DebuggerModelTestUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Bookmark;
import ghidra.program.model.listing.Program;
@@ -49,7 +50,8 @@ import ghidra.util.SystemUtilities;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.datastruct.ListenerMap;
public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDebuggerGUITest
implements DebuggerModelTestUtils {
protected static final long TIMEOUT_MILLIS =
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
@@ -1261,7 +1263,7 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
}
@Test
public void testPlaceDisableStepThenEnableTraceOnly() throws Exception {
public void testPlaceDisableStepThenEnableTraceOnly() throws Throwable {
startRecorder1();
Trace trace = recorder1.getTrace();
traceManager.openTrace(trace);
@@ -1274,24 +1276,25 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
assertLogicalBreakpointForLoneSoftwareBreakpoint(trace);
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
lb.disable();
waitOn(lb.disable());
waitForDomainObject(trace);
assertEquals(Enablement.DISABLED, lb.computeEnablement());
// Simulate a step, which should also cause snap advance in recorder
long oldSnap = recorder1.getSnap();
mb.testModel.session.simulateStep(mb.testThread1);
waitOn(mb.testModel.getClientExecutor());
assertEquals(oldSnap + 1, recorder1.getSnap());
assertEquals(Enablement.DISABLED, lb.computeEnablement());
lb.enable();
waitOn(lb.enable());
waitForDomainObject(trace);
assertEquals(Enablement.ENABLED, lb.computeEnablement());
}
@Test
public void testDeleteBreakpointTraceOnly() throws Exception {
public void testDeleteBreakpointTraceOnly() throws Throwable {
startRecorder1();
Trace trace = recorder1.getTrace();
traceManager.openTrace(trace);
@@ -1305,14 +1308,14 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
LogicalBreakpoint lb = Unique.assertOne(breakpointService.getAllBreakpoints());
lb.delete();
waitOn(lb.delete());
waitForDomainObject(trace);
assertTrue(breakpointService.getAllBreakpoints().isEmpty());
}
@Test
public void testPlaceStepThenDeleteBreakpointTraceOnly() throws Exception {
public void testPlaceStepThenDeleteBreakpointTraceOnly() throws Throwable {
startRecorder1();
Trace trace = recorder1.getTrace();
traceManager.openTrace(trace);
@@ -1329,9 +1332,10 @@ public class DebuggerLogicalBreakpointServiceTest extends AbstractGhidraHeadedDe
// Simulate a step, which should also cause snap advance in recorder
long oldSnap = recorder1.getSnap();
mb.testModel.session.simulateStep(mb.testThread1);
waitOn(mb.testModel.getClientExecutor());
assertEquals(oldSnap + 1, recorder1.getSnap());
lb.delete();
waitOn(lb.delete());
waitForDomainObject(trace);
assertTrue(breakpointService.getAllBreakpoints().isEmpty());
@@ -33,8 +33,7 @@ import ghidra.dbg.DebuggerModelFactory;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.model.TestDebuggerObjectModel;
import ghidra.dbg.model.TestLocalDebuggerModelFactory;
import ghidra.dbg.util.PathMatcher;
import ghidra.dbg.util.PathUtils;
import ghidra.dbg.util.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.SystemUtilities;
@@ -47,7 +46,8 @@ import mockit.VerificationsInOrder;
*
* TODO: Cover cases where multiple recorders are present
*/
public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITest {
public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITest
implements DebuggerModelTestUtils {
protected static final long TIMEOUT_MILLIS =
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
@@ -266,20 +266,23 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
}
@Test
public void testRecordThenCloseStopsRecording() throws Exception {
public void testRecordThenCloseStopsRecording() throws Throwable {
createTestModel();
mb.createTestProcessesAndThreads();
TraceRecorder recorder = modelService.recordTarget(mb.testProcess1,
new TestDebuggerTargetTraceMapper(mb.testProcess1));
assertNotNull(recorder);
waitOn(recorder.init()); // Already initializing, just wait for it to complete
CompletableFuture<Void> disconnect = mb.testModel.close();
disconnect.get();
waitForCondition(() -> !recorder.isRecording(), "Still recording");
waitOn(mb.testModel.close());
waitForPass(() -> {
assertFalse("Still recording", recorder.isRecording());
});
}
@Test
public void testRecordAndOpenThenCloseModelAndTraceLeavesNoConsumers() throws Exception {
public void testRecordAndOpenThenCloseModelAndTraceLeavesNoConsumers() throws Throwable {
createTestModel();
mb.createTestProcessesAndThreads();
@@ -291,9 +294,10 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
assertNotNull("No active trace", trace);
traceManager.closeTrace(trace);
CompletableFuture<Void> disconnect = mb.testModel.close();
disconnect.get();
assertEquals(List.of(), trace.getConsumerList());
waitOn(mb.testModel.close());
waitForPass(() -> {
assertEquals(List.of(), trace.getConsumerList());
});
}
@Test
@@ -359,7 +363,13 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
TraceThread traceThread = Unique.assertOne(
recorder.getTrace().getThreadManager().getThreadsByPath("Processes[1].Threads[1]"));
assertEquals(mb.testThread1, modelService.getTargetThread(traceThread));
/**
* There's a brief period where the trace thread exists, but it hasn't been entered into the
* recorder's internal map, yet. So, we have to wait.
*/
waitForPass(() -> {
assertEquals(mb.testThread1, modelService.getTargetThread(traceThread));
});
}
protected void doTestGetTraceThread(Runnable preRec, Runnable postRec) throws Exception {
@@ -429,7 +439,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
}
@Test
public void testTargetFocus() throws Exception {
public void testTargetFocus() throws Throwable {
createTestModel();
mb.createTestProcessesAndThreads();
@@ -442,19 +452,19 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
assertNull(modelService.getTargetFocus(mb.testProcess1));
assertNull(modelService.getTargetFocus(mb.testProcess3));
mb.testModel.requestFocus(mb.testThread1);
waitOn(mb.testModel.requestFocus(mb.testThread1));
assertEquals(mb.testThread1, modelService.getTargetFocus(mb.testProcess1));
assertNull(modelService.getTargetFocus(mb.testProcess3));
mb.testModel.requestFocus(mb.testThread2);
waitOn(mb.testModel.requestFocus(mb.testThread2));
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
assertNull(modelService.getTargetFocus(mb.testProcess3));
mb.testModel.requestFocus(mb.testThread3);
waitOn(mb.testModel.requestFocus(mb.testThread3));
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
assertEquals(mb.testThread3, modelService.getTargetFocus(mb.testProcess3));
mb.testModel.requestFocus(mb.testThread4);
waitOn(mb.testModel.requestFocus(mb.testThread4));
assertEquals(mb.testThread2, modelService.getTargetFocus(mb.testProcess1));
assertEquals(mb.testThread4, modelService.getTargetFocus(mb.testProcess3));
}
@@ -484,13 +494,13 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
}
@Test
public void testCurrentModelNullAfterClose() throws Exception {
public void testCurrentModelNullAfterClose() throws Throwable {
createTestModel();
modelService.activateModel(mb.testModel);
assertEquals(mb.testModel, modelService.getCurrentModel());
mb.testModel.close();
waitOn(mb.testModel.close());
assertNull(modelService.getCurrentModel());
}
}
@@ -61,8 +61,7 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
Language language = trace.getBaseLanguage();
SleighExpression expr = SleighProgramCompiler
.compileExpression((SleighLanguage) language,
"r0 + r1");
.compileExpression((SleighLanguage) language, "r0 + r1");
AsyncPcodeExecutor<byte[]> executor =
new AsyncPcodeExecutor<>(language, AsyncWrappedPcodeArithmetic.forLanguage(language),
@@ -94,10 +93,12 @@ public class TraceRecorderAsyncPcodeExecTest extends AbstractGhidraHeadedDebugge
SleighProgram prog = SleighProgramCompiler.compileProgram((SleighLanguage) language, "test",
List.of("r2 = r0 + r1;"), SleighUseropLibrary.NIL);
AsyncPcodeExecutor<byte[]> executor =
new AsyncPcodeExecutor<>(language, AsyncWrappedPcodeArithmetic.forLanguage(language),
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0));
TraceRecorderAsyncPcodeExecutorState asyncState =
new TraceRecorderAsyncPcodeExecutorState(recorder, recorder.getSnap(), thread, 0);
AsyncPcodeExecutor<byte[]> executor = new AsyncPcodeExecutor<>(
language, AsyncWrappedPcodeArithmetic.forLanguage(language), asyncState);
waitOn(executor.executeAsync(prog, SleighUseropLibrary.nil()));
waitOn(asyncState.getVar(language.getRegister("r2")));
assertEquals(BigInteger.valueOf(11), new BigInteger(1, regs.regVals.get("r2")));
}
@@ -15,8 +15,7 @@
*/
package ghidra.async;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
import ghidra.util.SystemUtilities;
@@ -32,4 +31,8 @@ public interface AsyncTestUtils {
throw AsyncUtils.unwrapThrowable(e);
}
}
default void waitOn(Executor executor) throws Throwable {
waitOn(CompletableFuture.supplyAsync(() -> null, executor));
}
}
@@ -21,6 +21,7 @@ import ghidra.util.classfinder.ExtensionPoint;
/**
* A factory for a debugger model
*
* <p>
* This provides a discoverable means of creating a debug model.
*/
public interface DebuggerModelFactory
@@ -509,4 +509,39 @@ public interface DebuggerObjectModel {
* @return the executor
*/
Executor getClientExecutor();
/**
* Ensure that dependent computations occur on the client executor
*
* <p>
* This also preserves scheduling order on the executor. Using just
* {@link CompletableFuture#thenApplyAsync(java.util.function.Function)} makes no guarantees
* about execution order, because that invocation could occur before invocations in the chained
* actions. This one instead uses
* {@link CompletableFuture#thenCompose(java.util.function.Function)} to schedule a final action
* which performs the actual completion via the executor.
*
* @param <T> the type of the future value
* @param cf the future
* @return a future gated via the client executor
*/
default <T> CompletableFuture<T> gateFuture(CompletableFuture<T> cf) {
return cf.thenCompose(this::gateFuture);
}
/**
* Ensure that dependent computations occur on the client executor
*
* <p>
* Use as a method reference in a final call to
* {@link CompletableFuture#thenCompose(java.util.function.Function)} to ensure the final stage
* completes on the client executor.
*
* @param <T> the type of the future value
* @param v the value
* @return a future while completes with the given value on the client executor
*/
default <T> CompletableFuture<T> gateFuture(T v) {
return CompletableFuture.supplyAsync(() -> v, getClientExecutor());
}
}
@@ -163,7 +163,7 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
}
req = curElemsRequest;
}
return req.thenApply(__ -> getCachedElements());
return req.thenApply(__ -> getCachedElements()).thenCompose(model::gateFuture);
}
@Override
@@ -337,7 +337,7 @@ public class DefaultTargetObject<E extends TargetObject, P extends TargetObject>
}
return getCachedAttributes();
}
});
}).thenCompose(model::gateFuture);
}
@Override
@@ -20,11 +20,12 @@ import static org.junit.Assert.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.CompletableFuture;
import org.junit.Test;
import generic.Unique;
import ghidra.async.AsyncTestUtils;
import ghidra.async.AsyncUtils;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.target.TargetObject;
@@ -34,11 +35,8 @@ import ghidra.dbg.util.ElementsChangedListener.ElementsChangedInvocation;
import ghidra.dbg.util.InvalidatedListener.InvalidatedInvocation;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressSpace;
import ghidra.util.SystemUtilities;
public class DefaultDebuggerObjectModelTest {
static final long TIMEOUT_MILLISECONDS =
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
public class DefaultDebuggerObjectModelTest implements AsyncTestUtils {
public static class FakeTargetObject extends DefaultTargetObject<TargetObject, TargetObject> {
public FakeTargetObject(DebuggerObjectModel model, TargetObject parent, String name) {
@@ -92,15 +90,6 @@ public class DefaultDebuggerObjectModelTest {
}
}
protected static <T> T waitOn(CompletableFuture<T> future) throws Throwable {
try {
return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
}
catch (ExecutionException e) {
throw e.getCause();
}
}
FakeDebuggerObjectModel model = new FakeDebuggerObjectModel();
@Test
@@ -187,6 +176,7 @@ public class DefaultDebuggerObjectModelTest {
PhonyTargetObject phonyA = new PhonyTargetObject(model, model.root, "A");
model.root.setAttributes(Map.of("A", phonyA), "Replace");
waitOn(model.clientExecutor);
// Object-valued attribute replacement requires prior removal
assertSame(fakeA, waitOn(model.fetchModelObject("A")));
@@ -197,6 +187,7 @@ public class DefaultDebuggerObjectModelTest {
// TODO: Should I permit custom equality check?
model.root.setAttributes(Map.of(), "Clear");
model.root.setAttributes(Map.of("A", phonyA), "Replace");
waitOn(model.clientExecutor);
assertEquals(2, attrL.invocations.size());
AttributesChangedInvocation changed = attrL.invocations.get(0);
@@ -57,7 +57,7 @@ public abstract class AbstractTestTargetRegisterBank<T extends AbstractTestTarge
return regs.getModel().future(result).thenApply(__ -> {
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result);
return result;
});
}).thenCompose(model::gateFuture);
}
protected CompletableFuture<Void> writeRegs(Map<String, byte[]> values,
@@ -81,7 +81,7 @@ public abstract class AbstractTestTargetRegisterBank<T extends AbstractTestTarge
}
future.thenAccept(__ -> {
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, updates);
});
}).thenCompose(model::gateFuture);
return future;
}
@@ -32,7 +32,7 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
CompletableFuture.delayedExecutor(DELAY_MILLIS, TimeUnit.MILLISECONDS);
enum FutureMode {
SYNC, ASYNC, DELAYED;
ASYNC, DELAYED;
}
protected final AddressSpace ram =
@@ -40,8 +40,6 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
protected final AddressFactory factory = new DefaultAddressFactory(new AddressSpace[] { ram });
public final TestTargetSession session;
protected TestDebuggerObjectModel.FutureMode futureMode = FutureMode.SYNC;
protected int invalidateCachesCount;
public TestDebuggerObjectModel() {
@@ -78,15 +76,7 @@ public class TestDebuggerObjectModel extends AbstractDebuggerObjectModel {
}
public <T> CompletableFuture<T> future(T t) {
switch (futureMode) {
case SYNC:
return CompletableFuture.completedFuture(t);
case ASYNC:
return CompletableFuture.supplyAsync(() -> t);
case DELAYED:
return CompletableFuture.supplyAsync(() -> t, DELAYED_EXECUTOR);
}
throw new AssertionError();
return CompletableFuture.supplyAsync(() -> t, getClientExecutor());
}
public CompletableFuture<Void> requestFocus(TargetObjectRef obj) {
@@ -64,7 +64,7 @@ public class TestTargetSession extends DefaultTargetModelRoot
FOCUS_ATTRIBUTE_NAME, obj //
), "Focus requested");
listeners.fire(TargetFocusScopeListener.class).focusChanged(this, obj);
});
}).thenCompose(model::gateFuture);
}
public void simulateStep(TestTargetThread eventThread) {
@@ -17,12 +17,18 @@ package ghidra.pcode.exec;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.program.model.pcode.Varnode;
public interface PcodeExecutorStatePiece<A, T> {
A longToOffset(AddressSpace space, long l);
default void setVar(Register reg, T val) {
Address address = reg.getAddress();
setVar(address.getAddressSpace(), address.getOffset(), reg.getMinimumByteSize(), true, val);
}
default void setVar(Varnode var, T val) {
Address address = var.getAddress();
setVar(address.getAddressSpace(), address.getOffset(), var.getSize(), true, val);
@@ -35,6 +41,12 @@ public interface PcodeExecutorStatePiece<A, T> {
setVar(space, longToOffset(space, offset), size, truncateAddressableUnit, val);
}
default T getVar(Register reg) {
Address address = reg.getAddress();
return getVar(address.getAddressSpace(), address.getOffset(), reg.getMinimumByteSize(),
true);
}
default T getVar(Varnode var) {
Address address = var.getAddress();
return getVar(address.getAddressSpace(), address.getOffset(), var.getSize(), true);