@@ -35,6 +35,21 @@ type AccessMock = {
3535 evaluateRegisterValue : jest . Mock ;
3636} ;
3737
38+ type TrackerWithCallbacks = {
39+ onContinued : ( cb : ( event : { session : GDBTargetDebugSession } ) => void ) => void ;
40+ onStopped : ( cb : ( event : { session : GDBTargetDebugSession } ) => void ) => void ;
41+ _continued ?: ( event : { session : GDBTargetDebugSession } ) => void ;
42+ _stopped ?: ( event : { session : GDBTargetDebugSession } ) => void ;
43+ } ;
44+
45+ const createTrackerWithCallbacks = ( ) : TrackerWithCallbacks => {
46+ const tracker : TrackerWithCallbacks = {
47+ onContinued : ( cb ) => { tracker . _continued = cb ; } ,
48+ onStopped : ( cb ) => { tracker . _stopped = cb ; } ,
49+ } ;
50+ return tracker ;
51+ } ;
52+
3853let accessMock : AccessMock ;
3954jest . mock ( '../../component-viewer-target-access' , ( ) => ( {
4055 ComponentViewerTargetAccess : jest . fn ( ( ) => accessMock ) ,
@@ -130,16 +145,7 @@ describe('scvd-debug-target', () => {
130145 } ) ;
131146
132147 it ( 'handles array length and running state tracking' , async ( ) => {
133- type TrackerWithCallbacks = {
134- onContinued : ( cb : ( event : { session : GDBTargetDebugSession } ) => void ) => void ;
135- onStopped : ( cb : ( event : { session : GDBTargetDebugSession } ) => void ) => void ;
136- _continued ?: ( event : { session : GDBTargetDebugSession } ) => void ;
137- _stopped ?: ( event : { session : GDBTargetDebugSession } ) => void ;
138- } ;
139- const tracker : TrackerWithCallbacks = {
140- onContinued : ( cb ) => { tracker . _continued = cb ; } ,
141- onStopped : ( cb ) => { tracker . _stopped = cb ; } ,
142- } ;
148+ const tracker = createTrackerWithCallbacks ( ) ;
143149
144150 const target = new ScvdDebugTarget ( ) ;
145151 target . init ( session , tracker as unknown as GDBTargetDebugTracker ) ;
@@ -161,6 +167,75 @@ describe('scvd-debug-target', () => {
161167 expect ( await target . getTargetIsRunning ( ) ) . toBe ( false ) ;
162168 } ) ;
163169
170+ it ( 'uses cached symbol metadata while running and skips new symbol evaluations' , async ( ) => {
171+ const tracker = createTrackerWithCallbacks ( ) ;
172+ const target = new ScvdDebugTarget ( ) ;
173+ target . init ( session , tracker as unknown as GDBTargetDebugTracker ) ;
174+
175+ accessMock . evaluateSymbolAddress . mockResolvedValue ( '0x200' ) ;
176+ accessMock . evaluateNumberOfArrayElements . mockResolvedValue ( 8 ) ;
177+ accessMock . evaluateSymbolSize . mockResolvedValue ( 32 ) ;
178+ accessMock . evaluateSymbolName . mockResolvedValue ( 'main' ) ;
179+ accessMock . evaluateSymbolContext . mockResolvedValue ( 'main.c:10' ) ;
180+
181+ await expect ( target . findSymbolAddress ( 'foo' ) ) . resolves . toBe ( 0x200 ) ;
182+ await expect ( target . getNumArrayElements ( 'foo' ) ) . resolves . toBe ( 8 ) ;
183+ await expect ( target . getSymbolSize ( 'foo' ) ) . resolves . toBe ( 32 ) ;
184+ await expect ( target . findSymbolNameAtAddress ( 0x200 ) ) . resolves . toBe ( 'main' ) ;
185+ await expect ( target . findSymbolContextAtAddress ( 0x200 ) ) . resolves . toBe ( 'main.c:10' ) ;
186+
187+ accessMock . evaluateSymbolAddress . mockClear ( ) ;
188+ accessMock . evaluateNumberOfArrayElements . mockClear ( ) ;
189+ accessMock . evaluateSymbolSize . mockClear ( ) ;
190+ accessMock . evaluateSymbolName . mockClear ( ) ;
191+ accessMock . evaluateSymbolContext . mockClear ( ) ;
192+ await tracker . _continued ?.( { session } ) ;
193+
194+ await expect ( target . findSymbolAddress ( 'foo' ) ) . resolves . toBe ( 0x200 ) ;
195+ await expect ( target . getNumArrayElements ( 'foo' ) ) . resolves . toBe ( 8 ) ;
196+ await expect ( target . getSymbolSize ( 'foo' ) ) . resolves . toBe ( 32 ) ;
197+ await expect ( target . findSymbolNameAtAddress ( 0x200 ) ) . resolves . toBe ( 'main' ) ;
198+ await expect ( target . findSymbolContextAtAddress ( 0x200 ) ) . resolves . toBe ( 'main.c:10' ) ;
199+
200+ await expect ( target . findSymbolAddress ( 'bar' ) ) . resolves . toBeUndefined ( ) ;
201+ await expect ( target . getNumArrayElements ( 'bar' ) ) . resolves . toBeUndefined ( ) ;
202+ await expect ( target . getSymbolSize ( 'bar' ) ) . resolves . toBeUndefined ( ) ;
203+ await expect ( target . findSymbolNameAtAddress ( 0x201 ) ) . resolves . toBeUndefined ( ) ;
204+ await expect ( target . findSymbolContextAtAddress ( 0x201 ) ) . resolves . toBeUndefined ( ) ;
205+
206+ expect ( accessMock . evaluateSymbolAddress ) . not . toHaveBeenCalled ( ) ;
207+ expect ( accessMock . evaluateNumberOfArrayElements ) . not . toHaveBeenCalled ( ) ;
208+ expect ( accessMock . evaluateSymbolSize ) . not . toHaveBeenCalled ( ) ;
209+ expect ( accessMock . evaluateSymbolName ) . not . toHaveBeenCalled ( ) ;
210+ expect ( accessMock . evaluateSymbolContext ) . not . toHaveBeenCalled ( ) ;
211+
212+ await tracker . _stopped ?.( { session } ) ;
213+ } ) ;
214+
215+ it ( 'treats unknown target state as unsafe and skips symbol evaluation on cache miss' , async ( ) => {
216+ const unknownSession = {
217+ session : { id : 'sess-unknown' } ,
218+ targetState : 'unknown'
219+ } as unknown as GDBTargetDebugSession ;
220+ const tracker = { onContinued : jest . fn ( ) , onStopped : jest . fn ( ) } as unknown as GDBTargetDebugTracker ;
221+ const target = new ScvdDebugTarget ( ) ;
222+ target . init ( unknownSession , tracker ) ;
223+
224+ await expect ( target . getTargetIsRunning ( ) ) . resolves . toBe ( true ) ;
225+
226+ accessMock . evaluateSymbolAddress . mockResolvedValue ( '0x200' ) ;
227+ accessMock . evaluateSymbolName . mockResolvedValue ( 'main' ) ;
228+ accessMock . evaluateSymbolContext . mockResolvedValue ( 'main.c:10' ) ;
229+
230+ await expect ( target . findSymbolAddress ( 'foo' ) ) . resolves . toBeUndefined ( ) ;
231+ await expect ( target . findSymbolNameAtAddress ( 0x200 ) ) . resolves . toBeUndefined ( ) ;
232+ await expect ( target . findSymbolContextAtAddress ( 0x200 ) ) . resolves . toBeUndefined ( ) ;
233+
234+ expect ( accessMock . evaluateSymbolAddress ) . not . toHaveBeenCalled ( ) ;
235+ expect ( accessMock . evaluateSymbolName ) . not . toHaveBeenCalled ( ) ;
236+ expect ( accessMock . evaluateSymbolContext ) . not . toHaveBeenCalled ( ) ;
237+ } ) ;
238+
164239 it ( 'finds symbol address and size' , async ( ) => {
165240 accessMock . evaluateSymbolAddress . mockResolvedValue ( '0x200' ) ;
166241 accessMock . evaluateSymbolSize . mockResolvedValue ( 16 ) ;
@@ -312,6 +387,17 @@ describe('scvd-debug-target', () => {
312387 await expect ( target . readRegister ( undefined as unknown as string ) ) . resolves . toBeUndefined ( ) ;
313388 } ) ;
314389
390+ it ( 'skips register reads while running' , async ( ) => {
391+ const tracker = createTrackerWithCallbacks ( ) ;
392+ const target = new ScvdDebugTarget ( ) ;
393+ target . init ( session , tracker as unknown as GDBTargetDebugTracker ) ;
394+ await tracker . _continued ?.( { session } ) ;
395+
396+ accessMock . evaluateRegisterValue . mockResolvedValue ( 1 ) ;
397+ await expect ( target . readRegister ( 'r0' ) ) . resolves . toBeUndefined ( ) ;
398+ expect ( accessMock . evaluateRegisterValue ) . not . toHaveBeenCalled ( ) ;
399+ } ) ;
400+
315401 it ( 'covers cache hits, misses, and normalization paths' , async ( ) => {
316402 const tracker = { onContinued : jest . fn ( ) , onStopped : jest . fn ( ) } as unknown as GDBTargetDebugTracker ;
317403 const target = new ScvdDebugTarget ( ) ;
0 commit comments