diff --git a/CHANGELOG.md b/CHANGELOG.md index fccc860..468cadf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fix app state not being properly reset when stepping back ([#19](https://github.com/algorand/avm-debugger/pull/19)) + ## [0.1.2] - 2023-12-14 ### Fixed diff --git a/src/common/traceReplayEngine.ts b/src/common/traceReplayEngine.ts index 452ff2b..88c08cf 100644 --- a/src/common/traceReplayEngine.ts +++ b/src/common/traceReplayEngine.ts @@ -981,7 +981,7 @@ export class ProgramStackFrame extends TraceReplayStackFrame { if (typeof this.initialAppState !== 'undefined') { this.engine.currentAppState.set( this.currentAppID()!, - this.initialAppState, + this.initialAppState.clone(), ); } } diff --git a/tests/adapter.test.ts b/tests/adapter.test.ts index f5a0b91..4ed5d7f 100644 --- a/tests/adapter.test.ts +++ b/tests/adapter.test.ts @@ -1402,6 +1402,35 @@ describe('Debug Adapter Tests', () => { }, ], }); + + // Move to the end of the program + await advanceTo(client, { program: PROGRAM, line: 46 }); + + // Step back to the beginning of the program + for (;;) { + const stackTraceResponse = await client.stackTraceRequest({ + threadId: 1, + }); + const currentFrame = stackTraceResponse.body.stackFrames[0]; + if (currentFrame.source?.path === PROGRAM && currentFrame.line === 3) { + break; + } + await client.stepBackRequest({ threadId: 1 }); + const stoppedEvent = await client.waitForStop(); + assert.strictEqual(stoppedEvent.body.reason, 'step'); + } + + // Ensure that the global state at the beginning does not show changes that will happen later + await assertVariables(client, { + pc: 6, + stack: [1050], + apps: [ + { + appID: 1050, + globalState: new ByteArrayMap(), + }, + ], + }); }); }); @@ -1535,6 +1564,41 @@ describe('Debug Adapter Tests', () => { }, ], }); + + // Move to the end of the program + await advanceTo(client, { program: PROGRAM, line: 46 }); + + // Step back to the beginning of the program + for (;;) { + const stackTraceResponse = await client.stackTraceRequest({ + threadId: 1, + }); + const currentFrame = stackTraceResponse.body.stackFrames[0]; + if (currentFrame.source?.path === PROGRAM && currentFrame.line === 3) { + break; + } + await client.stepBackRequest({ threadId: 1 }); + const stoppedEvent = await client.waitForStop(); + assert.strictEqual(stoppedEvent.body.reason, 'step'); + } + + // Ensure that the local state at the beginning does not show changes that will happen later + await assertVariables(client, { + pc: 6, + stack: [1054], + apps: [ + { + appID: 1054, + localState: [ + { + account: + 'YGOSQB6R5IVQDJHJUHTIZAJNWNIT7VLMWHXFWY2H5HMWPK7QOPXHELNPJ4', + state: new ByteArrayMap(), + }, + ], + }, + ], + }); }); }); @@ -1632,6 +1696,43 @@ describe('Debug Adapter Tests', () => { }, ], }); + + // Clear breakpoints -- must do because the 'next' request is on line 46 as well + await client.setBreakpointsRequest({ + source: { path: PROGRAM }, + breakpoints: [], + }); + + // Move to the end of the program + await client.nextRequest({ threadId: 1 }); + const stoppedEvent = await client.waitForStop(); + assert.strictEqual(stoppedEvent.body.reason, 'step'); + + // Step back to the beginning of the program + for (;;) { + const stackTraceResponse = await client.stackTraceRequest({ + threadId: 1, + }); + const currentFrame = stackTraceResponse.body.stackFrames[0]; + if (currentFrame.source?.path === PROGRAM && currentFrame.line === 3) { + break; + } + await client.stepBackRequest({ threadId: 1 }); + const stoppedEvent = await client.waitForStop(); + assert.strictEqual(stoppedEvent.body.reason, 'step'); + } + + // Ensure that the box state at the beginning does not show changes that will happen later + await assertVariables(client, { + pc: 6, + stack: [1058], + apps: [ + { + appID: 1058, + boxState: new ByteArrayMap(), + }, + ], + }); }); });