Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dwds/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Bump `build_web_compilers` to ^4.4.1.
- Remove unused `clientFuture` arg from `DwdsVmClient` methods.
- Fix pausing starting of `main` after the hot restart.
- Cache javascript breakpoint ids to prevent 'Breakpoint already exists' errors.

## 26.2.2

Expand Down
65 changes: 51 additions & 14 deletions dwds/lib/src/debugging/debugger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,20 @@ class _Breakpoints {
final _dartIdByJsId = <String, String>{};
final _jsIdByDartId = <String, String>{};

/// JS location -> JS breakpoint ID
final _jsIdByJsLocKey = <String, String>{};

/// JS breakpoint ID -> JS location
final _jsLocKeyByJsId = <String, String>{};

String _jsLocKey(JsLocation location) =>
'${location.runtimeScriptId}:${location.line}:${location.column}';

String? _lookupExistingJsBreakpoint(JsLocation location) {
final jsLocKey = _jsLocKey(location);
return _jsIdByJsLocKey[jsLocKey];
}

final _bpByDartId = <String, Future<Breakpoint>>{};

final _queue = AtomicQueue();
Expand Down Expand Up @@ -835,7 +849,11 @@ class _Breakpoints {
'cannot set JS breakpoint at $location',
);
}
_note(jsId: jsBreakpointId, bp: dartBreakpoint);
_note(
jsId: jsBreakpointId,
bp: dartBreakpoint,
jsLocation: location.jsLocation,
);
return dartBreakpoint;
} on WipError catch (wipError) {
throw RPCError('addBreakpoint', 102, '$wipError');
Expand Down Expand Up @@ -880,18 +898,19 @@ class _Breakpoints {
return _queue.run(() async {
final scriptId = location.jsLocation.runtimeScriptId;
if (scriptId != null) {
return sendCommandAndValidateResult<String>(
remoteDebugger,
method: 'Debugger.setBreakpoint',
resultField: 'breakpointId',
params: {
'location': {
'lineNumber': location.jsLocation.line,
'columnNumber': location.jsLocation.column,
'scriptId': scriptId,
},
},
);
return _lookupExistingJsBreakpoint(location.jsLocation) ??
await sendCommandAndValidateResult<String>(
remoteDebugger,
method: 'Debugger.setBreakpoint',
resultField: 'breakpointId',
params: {
'location': {
'lineNumber': location.jsLocation.line,
'columnNumber': location.jsLocation.column,
'scriptId': scriptId,
},
},
);
} else {
_logger.fine('No runtime script ID for location $location');
return null;
Expand All @@ -901,11 +920,24 @@ class _Breakpoints {

/// Records the internal Dart <=> JS breakpoint id mapping and adds the
/// breakpoint to the current isolates list of breakpoints.
void _note({required Breakpoint bp, required String jsId}) {
void _note({
required Breakpoint bp,
required String jsId,
required JsLocation jsLocation,
}) {
final bpId = bp.id;
if (bpId != null) {
_dartIdByJsId[jsId] = bpId;
_jsIdByDartId[bpId] = jsId;

// Cache JS breakpoint id.
final jsLocKey = _jsLocKey(jsLocation);
if (_jsIdByJsLocKey.containsKey(jsLocKey)) {
_logger.fine('Reused existing JS breakpoint $jsId for $jsLocKey');
}
_jsIdByJsLocKey[jsLocKey] = jsId;
_jsLocKeyByJsId[jsId] = jsLocKey;

final isolate = inspector.isolate;
isolate.breakpoints?.add(bp);
}
Expand All @@ -916,6 +948,11 @@ class _Breakpoints {
required String dartId,
}) async {
final isolate = inspector.isolate;
// Clear from JS location caches
final jsLocKey = _jsLocKeyByJsId.remove(jsId);
if (jsLocKey != null) {
_jsIdByJsLocKey.remove(jsLocKey);
}
_dartIdByJsId.remove(jsId);
_jsIdByDartId.remove(dartId);
isolate.breakpoints?.removeWhere((b) => b.id == dartId);
Expand Down