Skip to content

Commit 6ba7f5b

Browse files
committed
2 parents fa5feb0 + 4dbddc8 commit 6ba7f5b

File tree

114 files changed

+2102
-1279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+2102
-1279
lines changed

dwds/CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
## 24.3.12-wip
1+
## 24.4.1-wip
22

33
- Implemented a WebSocket-based communication protocol that provides essential developer tooling (hot reload, service extensions) when Chrome debugger access is unavailable. - [#2605](https://github.com/dart-lang/webdev/issues/2605)
44
- Added WebSocket-based hot reload and service extension support via new `WebSocketProxyService` class that implements VM service protocol over WebSockets.
5-
- Created new files: `WebSocketProxyService`, `WebSocketDebugService`, `WebSocketAppDebugServices`, and `WebSocketDwdsVmClient` to support socket-based DWDS functionality.
65
- Enhanced `DevHandler` with `useWebSocketConnection` flag to toggle between Chrome-based and WebSocket-based communication protocols.
76

7+
8+
## 24.4.0
9+
10+
- Added support for breakpoint registering on a hot reload with the DDC library bundle format using PausePostRequests.
11+
- `FrontendServerDdcLibraryBundleStrategy.hotReloadSourceUri` is now expected to also provide the reloaded modules.
12+
813
## 24.3.11
914

1015
- Changed DWDS to always inject the client and added `useDwdsWebSocketConnection` flag to control communication protocol: when true uses socket-based implementation, when false uses Chrome-based communication protocol.

dwds/lib/src/debugging/debugger.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ class Debugger extends Domain {
594594
params: {
595595
'skipList': _skipLists.compute(
596596
scriptId,
597+
url,
597598
await _locations.locationsForUrl(url),
598599
),
599600
},

dwds/lib/src/debugging/inspector.dart

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:dwds/src/debugging/execution_context.dart';
1313
import 'package:dwds/src/debugging/instance.dart';
1414
import 'package:dwds/src/debugging/libraries.dart';
1515
import 'package:dwds/src/debugging/location.dart';
16+
import 'package:dwds/src/debugging/metadata/provider.dart';
1617
import 'package:dwds/src/debugging/remote_debugger.dart';
1718
import 'package:dwds/src/loaders/ddc_library_bundle.dart';
1819
import 'package:dwds/src/readers/asset_reader.dart';
@@ -32,9 +33,11 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
3233
/// Provides information about currently loaded scripts and objects and support
3334
/// for eval.
3435
class AppInspector implements AppInspectorInterface {
35-
final _scriptCacheMemoizer = AsyncMemoizer<List<ScriptRef>>();
36+
var _scriptCacheMemoizer = AsyncMemoizer<List<ScriptRef>>();
3637

37-
Future<List<ScriptRef>> get scriptRefs => _populateScriptCaches();
38+
Future<List<ScriptRef>> getScriptRefs({
39+
ModifiedModuleReport? modifiedModuleReport,
40+
}) => _populateScriptCaches(modifiedModuleReport: modifiedModuleReport);
3841

3942
final _logger = Logger('AppInspector');
4043

@@ -68,9 +71,9 @@ class AppInspector implements AppInspectorInterface {
6871

6972
final ExecutionContext _executionContext;
7073

71-
late final LibraryHelper _libraryHelper;
72-
late final ClassHelper _classHelper;
73-
late final InstanceHelper _instanceHelper;
74+
late LibraryHelper _libraryHelper;
75+
late ClassHelper _classHelper;
76+
late InstanceHelper _instanceHelper;
7477

7578
final AssetReader _assetReader;
7679
final Locations _locations;
@@ -99,18 +102,39 @@ class AppInspector implements AppInspectorInterface {
99102
this._locations,
100103
this._root,
101104
this._executionContext,
102-
) : _isolateRef = _toIsolateRef(_isolate) {
103-
_libraryHelper = LibraryHelper(this);
105+
) : _isolateRef = _toIsolateRef(_isolate);
106+
107+
/// Reset all caches and recompute any mappings.
108+
///
109+
/// Should be called across hot reloads with a valid [ModifiedModuleReport].
110+
Future<void> initialize({ModifiedModuleReport? modifiedModuleReport}) async {
111+
_scriptCacheMemoizer = AsyncMemoizer<List<ScriptRef>>();
112+
113+
// TODO(srujzs): We can invalidate these in a smarter way instead of
114+
// reinitializing when doing a hot reload, but these helpers recompute info
115+
// on demand later and therefore are not in the critical path.
104116
_classHelper = ClassHelper(this);
105117
_instanceHelper = InstanceHelper(this);
106-
}
107118

108-
Future<void> initialize() async {
119+
if (modifiedModuleReport != null) {
120+
// Invalidate `_libraryHelper` as we use it populate any script caches.
121+
_libraryHelper.initialize(modifiedModuleReport: modifiedModuleReport);
122+
} else {
123+
_libraryHelper = LibraryHelper(this)..initialize();
124+
_scriptRefsById.clear();
125+
_serverPathToScriptRef.clear();
126+
_scriptIdToLibraryId.clear();
127+
_libraryIdToScriptRefs.clear();
128+
}
129+
109130
final libraries = await _libraryHelper.libraryRefs;
110131
isolate.rootLib = await _libraryHelper.rootLib;
132+
isolate.libraries?.clear();
111133
isolate.libraries?.addAll(libraries);
112134

113-
final scripts = await scriptRefs;
135+
final scripts = await getScriptRefs(
136+
modifiedModuleReport: modifiedModuleReport,
137+
);
114138

115139
await DartUri.initialize();
116140
DartUri.recordAbsoluteUris(libraries.map((lib) => lib.uri).nonNulls);
@@ -573,7 +597,7 @@ class AppInspector implements AppInspectorInterface {
573597
/// All the scripts in the isolate.
574598
@override
575599
Future<ScriptList> getScripts() async {
576-
return ScriptList(scripts: await scriptRefs);
600+
return ScriptList(scripts: await getScriptRefs());
577601
}
578602

579603
/// Calls the Chrome Runtime.getProperties API for the object with [objectId].
@@ -702,22 +726,52 @@ class AppInspector implements AppInspectorInterface {
702726
/// This populates [_scriptRefsById], [_scriptIdToLibraryId],
703727
/// [_libraryIdToScriptRefs] and [_serverPathToScriptRef].
704728
///
705-
/// It is a one-time operation, because if we do a
706-
/// reload the inspector will get re-created.
729+
/// This will get repopulated on restarts and reloads.
730+
///
731+
/// If [modifiedModuleReport] is provided, only invalidates and
732+
/// recalculates caches for the modified libraries.
707733
///
708734
/// Returns the list of scripts refs cached.
709-
Future<List<ScriptRef>> _populateScriptCaches() {
735+
Future<List<ScriptRef>> _populateScriptCaches({
736+
ModifiedModuleReport? modifiedModuleReport,
737+
}) {
710738
return _scriptCacheMemoizer.runOnce(() async {
711739
final scripts =
712740
await globalToolConfiguration.loadStrategy
713741
.metadataProviderFor(appConnection.request.entrypointPath)
714742
.scripts;
743+
if (modifiedModuleReport != null) {
744+
// Invalidate any script caches that were computed for the now invalid
745+
// libraries. They will get repopulated below.
746+
for (final libraryUri in modifiedModuleReport.modifiedLibraries) {
747+
final libraryRef = await _libraryHelper.libraryRefFor(libraryUri);
748+
final libraryId = libraryRef?.id;
749+
// If this was not a pre-existing library, nothing to invalidate.
750+
if (libraryId == null) continue;
751+
final scriptRefs = _libraryIdToScriptRefs.remove(libraryId);
752+
if (scriptRefs == null) continue;
753+
for (final scriptRef in scriptRefs) {
754+
final scriptId = scriptRef.id;
755+
final scriptUri = scriptRef.uri;
756+
if (scriptId != null && scriptUri != null) {
757+
_scriptRefsById.remove(scriptId);
758+
_scriptIdToLibraryId.remove(scriptId);
759+
_serverPathToScriptRef.remove(
760+
DartUri(scriptUri, _root).serverPath,
761+
);
762+
}
763+
}
764+
}
765+
}
715766
// For all the non-dart: libraries, find their parts and create scriptRefs
716767
// for them.
717768
final userLibraries = _userLibraryUris(
718769
isolate.libraries ?? <LibraryRef>[],
719770
);
720771
for (final uri in userLibraries) {
772+
if (modifiedModuleReport?.modifiedLibraries.contains(uri) == false) {
773+
continue;
774+
}
721775
final parts = scripts[uri];
722776
final scriptRefs = [
723777
ScriptRef(uri: uri, id: createId()),

dwds/lib/src/debugging/libraries.dart

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:collection/collection.dart';
66
import 'package:dwds/src/config/tool_configuration.dart';
77
import 'package:dwds/src/debugging/metadata/class.dart';
8+
import 'package:dwds/src/debugging/metadata/provider.dart';
89
import 'package:dwds/src/services/chrome_debug_exception.dart';
910
import 'package:dwds/src/utilities/domain.dart';
1011
import 'package:logging/logging.dart';
@@ -27,6 +28,29 @@ class LibraryHelper extends Domain {
2728
inspector = appInspector;
2829
}
2930

31+
/// Initialize any caches.
32+
///
33+
/// If [modifiedModuleReport] is not null, invalidates only modified libraries
34+
/// from the cache and recomputes values for any eager caches.
35+
void initialize({ModifiedModuleReport? modifiedModuleReport}) {
36+
_rootLib = null;
37+
if (modifiedModuleReport != null) {
38+
for (final library in modifiedModuleReport.modifiedLibraries) {
39+
// These will later be initialized by `libraryFor` if needed.
40+
_librariesById.remove(library);
41+
_libraryRefsById.remove(library);
42+
}
43+
for (final library in modifiedModuleReport.reloadedLibraries) {
44+
// These need to be recomputed here as `libraryRefs` only checks if this
45+
// map is empty before returning.
46+
_libraryRefsById[library] = _createLibraryRef(library);
47+
}
48+
return;
49+
}
50+
_librariesById.clear();
51+
_libraryRefsById.clear();
52+
}
53+
3054
Future<LibraryRef> get rootLib async {
3155
if (_rootLib != null) return _rootLib!;
3256
final libraries = await libraryRefs;
@@ -51,21 +75,21 @@ class LibraryHelper extends Domain {
5175
return _rootLib!;
5276
}
5377

78+
LibraryRef _createLibraryRef(String library) =>
79+
LibraryRef(id: library, name: library, uri: library);
80+
5481
/// Returns all libraryRefs in the app.
5582
///
56-
/// Note this can return a cached result.
83+
/// Note this can return a cached result that can be selectively reinitialized
84+
/// using [initialize].
5785
Future<List<LibraryRef>> get libraryRefs async {
5886
if (_libraryRefsById.isNotEmpty) return _libraryRefsById.values.toList();
5987
final libraries =
6088
await globalToolConfiguration.loadStrategy
6189
.metadataProviderFor(inspector.appConnection.request.entrypointPath)
6290
.libraries;
6391
for (final library in libraries) {
64-
_libraryRefsById[library] = LibraryRef(
65-
id: library,
66-
name: library,
67-
uri: library,
68-
);
92+
_libraryRefsById[library] = _createLibraryRef(library);
6993
}
7094
return _libraryRefsById.values.toList();
7195
}

dwds/lib/src/debugging/location.dart

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:async/async.dart';
66
import 'package:dwds/src/config/tool_configuration.dart';
7+
import 'package:dwds/src/debugging/metadata/provider.dart';
78
import 'package:dwds/src/debugging/modules.dart';
89
import 'package:dwds/src/readers/asset_reader.dart';
910
import 'package:dwds/src/utilities/dart_uri.dart';
@@ -151,11 +152,34 @@ class Locations {
151152

152153
Modules get modules => _modules;
153154

154-
void initialize(String entrypoint) {
155-
_sourceToTokenPosTable.clear();
156-
_sourceToLocation.clear();
155+
/// Initialize any caches.
156+
///
157+
/// If [modifiedModuleReport] is not null, only invalidates the caches for the
158+
/// modified modules instead.
159+
Future<void> initialize(
160+
String entrypoint, {
161+
ModifiedModuleReport? modifiedModuleReport,
162+
}) async {
163+
// If we know that only certain modules are deleted or added, we can only
164+
// invalidate those.
165+
if (modifiedModuleReport != null) {
166+
for (final module in modifiedModuleReport.modifiedModules) {
167+
_locationMemoizer.remove(module);
168+
_moduleToLocations.remove(module);
169+
final sources = await _modules.sourcesForModule(module);
170+
if (sources != null) {
171+
for (final serverPath in sources) {
172+
_sourceToTokenPosTable.remove(serverPath);
173+
_sourceToLocation.remove(serverPath);
174+
}
175+
}
176+
}
177+
return;
178+
}
157179
_locationMemoizer.clear();
158180
_moduleToLocations.clear();
181+
_sourceToTokenPosTable.clear();
182+
_sourceToLocation.clear();
159183
_entrypoint = entrypoint;
160184
}
161185

0 commit comments

Comments
 (0)