@@ -356,7 +356,7 @@ class IsolateManager {
356356
357357 // Finally, when we're resuming, all stored objects become invalid and
358358 // we can drop them to save memory.
359- thread.clearStoredData ();
359+ await thread.clearTemporaryData ();
360360
361361 thread.hasPendingUserResume = true ;
362362 try {
@@ -400,13 +400,14 @@ class IsolateManager {
400400 }
401401 }
402402
403- // When we're resuming, all stored objects become invalid and we can drop
404- // to save memory.
405- thread.clearStoredData ();
406-
403+ final isolateId = thread.isolate.id! ;
407404 try {
408- thread.hasPendingDapResume = true ;
409- await _adapter.vmService? .readyToResume (thread.isolate.id! );
405+ // When we're resuming, all stored objects become invalid and we can drop
406+ // to save memory.
407+ await thread.clearTemporaryData ();
408+
409+ // Finally, signal that we're ready to resume.
410+ await _adapter.vmService? .readyToResume (isolateId);
410411 } on UnimplementedError {
411412 // Fallback to a regular resume if the DDS version doesn't support
412413 // `readyToResume`:
@@ -426,8 +427,6 @@ class IsolateManager {
426427 } else {
427428 rethrow ;
428429 }
429- } finally {
430- thread.hasPendingDapResume = false ;
431430 }
432431 }
433432
@@ -570,12 +569,7 @@ class IsolateManager {
570569 String type,
571570 ) async {
572571 try {
573- final result = await _adapter.vmService? .evaluateInFrame (
574- thread.isolate.id! ,
575- 0 ,
576- expression,
577- disableBreakpoints: true ,
578- );
572+ final result = await _adapter.vmEvaluateInFrame (thread, 0 , expression);
579573
580574 if (result is vm.InstanceRef ) {
581575 return result;
@@ -1118,6 +1112,32 @@ class ThreadInfo with FileUtils {
11181112 var atAsyncSuspension = false ;
11191113 int ? exceptionReference;
11201114
1115+ /// A [Completer] that completes with the evaluation zone ID for this thread.
1116+ ///
1117+ /// The completer is created when the request to create an evaluation zone is
1118+ /// started (which is lazy, the first time evaluation is performed).
1119+ ///
1120+ /// When the Debug Adapter is ready to resume this Isolate, it will first
1121+ /// invalidate all evaluation IDs in this zone so that they can be collected.
1122+ /// If the [Completer] is null, no evaluation has occurred and invalidation
1123+ /// can be skipped.
1124+ Completer <String ?>? _currentEvaluationZoneIdCompleter;
1125+
1126+ /// Returns the current evaluation zone ID.
1127+ ///
1128+ /// To avoid additional 'await's, may return a String? directly if the value
1129+ /// is already available.
1130+ FutureOr <String ?> get currentEvaluationZoneId {
1131+ // We already have the value, avoid the Future.
1132+ if (_currentEvaluationZoneId != null ) {
1133+ return _currentEvaluationZoneId;
1134+ }
1135+ return _createOrGetEvaluationZoneId ();
1136+ }
1137+
1138+ /// The current evaluation zone ID (if available).
1139+ String ? _currentEvaluationZoneId;
1140+
11211141 /// Whether this thread is currently known to be paused in the VM.
11221142 ///
11231143 /// Because requests are async, this is not guaranteed to be always correct
@@ -1165,10 +1185,6 @@ class ThreadInfo with FileUtils {
11651185 /// has not yet been responded to.
11661186 var hasPendingUserResume = false ;
11671187
1168- /// Whether this isolate has an in-flight DAP (readyToResume) resume request
1169- /// that has not yet been responded to.
1170- var hasPendingDapResume = false ;
1171-
11721188 ThreadInfo (this ._manager, this .threadId, this .isolate);
11731189
11741190 Future <T > getObject< T extends vm.Response > (vm.ObjRef ref) =>
@@ -1188,6 +1204,42 @@ class ThreadInfo with FileUtils {
11881204 return _manager.getScripts (isolate);
11891205 }
11901206
1207+ /// Returns the evaluation zone ID for this thread.
1208+ ///
1209+ /// If it has not been created yet, creates it. If creation is in progress,
1210+ /// returns the existing future.
1211+ Future <String ?> _createOrGetEvaluationZoneId () async {
1212+ // If we already have a completer, the request is already in flight (or
1213+ // has completed).
1214+ var completer = _currentEvaluationZoneIdCompleter;
1215+ if (completer != null ) {
1216+ return completer.future;
1217+ }
1218+
1219+ // Otherwise, we need to start the request.
1220+ _currentEvaluationZoneIdCompleter = completer = Completer ();
1221+
1222+ try {
1223+ final response = await _manager._adapter.vmService? .createIdZone (
1224+ isolate.id! ,
1225+ vm.IdZoneBackingBufferKind .kRing,
1226+ vm.IdAssignmentPolicy .kAlwaysAllocate,
1227+ // Default capacity is 512. Since these are short-lived (only while
1228+ // paused) and we don't want to prevent expanding Lists, use something a
1229+ // little bigger.
1230+ capacity: 2048 ,
1231+ );
1232+ _currentEvaluationZoneId = response? .id;
1233+ } catch (_) {
1234+ // If this request fails for any reason (perhaps the target VM does not
1235+ // support this request), we should just use `null` as the zone ID and not
1236+ // prevent any evaluation requests.
1237+ _currentEvaluationZoneId = null ;
1238+ }
1239+ completer.complete (_currentEvaluationZoneId);
1240+ return _currentEvaluationZoneId;
1241+ }
1242+
11911243 /// Resolves a source file path (or URI) into a URI for the VM.
11921244 ///
11931245 /// sdk-path/lib/core/print.dart -> dart:core/print.dart
@@ -1489,11 +1541,32 @@ class ThreadInfo with FileUtils {
14891541 pathSegments: fileLikeUri.pathSegments.sublist (0 , keepSegments));
14901542 }
14911543
1492- /// Clears all data stored for this thread.
1544+ /// Clears all temporary stored for this thread. This includes:
1545+ ///
1546+ /// - dropping any variablesReferences
1547+ /// - invalidating the evaluation ID zone
1548+ ///
1549+ /// This is generally called when requesting execution continues, since any
1550+ /// evaluated references are not expected to live past this point.
14931551 ///
1494- /// References to stored data become invalid when the thread is resumed.
1495- void clearStoredData () {
1552+ /// https://microsoft.github.io/debug-adapter-protocol/overview#lifetime-of-objects-references
1553+ Future <void > clearTemporaryData () async {
1554+ // Clear variablesReferences.
14961555 _manager.clearStoredData (this );
1556+
1557+ // Invalidate all existing references in this evaluation zone.
1558+ // If the completer is null, no zone has ever been created (or started to
1559+ // be created), so this can be skipped.
1560+ if (_currentEvaluationZoneIdCompleter != null ) {
1561+ final futureOrEvalZoneId = currentEvaluationZoneId;
1562+ final evalZoneId = futureOrEvalZoneId is String
1563+ ? futureOrEvalZoneId
1564+ : await futureOrEvalZoneId;
1565+ if (evalZoneId != null ) {
1566+ await _manager._adapter.vmService
1567+ ? .invalidateIdZone (isolate.id! , evalZoneId);
1568+ }
1569+ }
14971570 }
14981571
14991572 /// Attempts to get a [vm.LibraryRef] for the given [scriptFileUri] .
0 commit comments