@@ -15,15 +15,14 @@ class MetadataProvider {
1515 final AssetReader _assetReader;
1616 final _logger = Logger ('MetadataProvider' );
1717 final String entrypoint;
18- final List <String > _libraries = [] ;
18+ final Set <String > _libraries = {} ;
1919 final Map <String , String > _scriptToModule = {};
2020 final Map <String , String > _moduleToSourceMap = {};
2121 final Map <String , String > _modulePathToModule = {};
2222 final Map <String , String > _moduleToModulePath = {};
23+ final Map <String , Set <String >> _moduleToLibraries = {};
2324 final Map <String , List <String >> _scripts = {};
2425 final _metadataMemoizer = AsyncMemoizer ();
25- // Whether to use the `name` provided in the module metadata.
26- final bool _useModuleName;
2726
2827 /// Implicitly imported libraries in any DDC component.
2928 ///
@@ -66,11 +65,7 @@ class MetadataProvider {
6665 'dart:ui' ,
6766 ];
6867
69- MetadataProvider (
70- this .entrypoint,
71- this ._assetReader, {
72- required bool useModuleName,
73- }) : _useModuleName = useModuleName;
68+ MetadataProvider (this .entrypoint, this ._assetReader);
7469
7570 /// A sound null safety mode for the whole app.
7671 ///
@@ -81,17 +76,17 @@ class MetadataProvider {
8176 return true ;
8277 }
8378
84- /// A list of all libraries in the Dart application.
79+ /// A set of all libraries in the Dart application.
8580 ///
8681 /// Example:
8782 ///
88- /// [
83+ /// {
8984 /// dart:web_gl,
9085 /// dart:math,
9186 /// org-dartlang-app:///web/main.dart
92- /// ]
87+ /// }
9388 ///
94- Future <List <String >> get libraries async {
89+ Future <Set <String >> get libraries async {
9590 await _initialize ();
9691 return _libraries;
9792 }
@@ -118,9 +113,6 @@ class MetadataProvider {
118113 /// org-dartlang-app:///web/main.dart :
119114 /// web/main
120115 /// }
121- ///
122- /// If [_useModuleName] is false, the values will be the module paths instead
123- /// of the name except for 'dart_sdk'.
124116 Future <Map <String , String >> get scriptToModule async {
125117 await _initialize ();
126118 return _scriptToModule;
@@ -134,9 +126,6 @@ class MetadataProvider {
134126 /// org-dartlang-app:///web/main.dart :
135127 /// web/main.ddc.js.map
136128 /// }
137- ///
138- /// If [_useModuleName] is false, the keys will be the module paths instead of
139- /// the name.
140129 Future <Map <String , String >> get moduleToSourceMap async {
141130 await _initialize ();
142131 return _moduleToSourceMap;
@@ -150,9 +139,6 @@ class MetadataProvider {
150139 /// web/main.ddc.js :
151140 /// web/main
152141 /// }
153- ///
154- /// If [_useModuleName] is false, the values will be the module paths instead
155- /// of the name, making this an identity map.
156142 Future <Map <String , String >> get modulePathToModule async {
157143 await _initialize ();
158144 return _modulePathToModule;
@@ -166,9 +152,6 @@ class MetadataProvider {
166152 /// web/main
167153 /// web/main.ddc.js :
168154 /// }
169- ///
170- /// If [_useModuleName] is false, the keys will be the module paths instead of
171- /// the name, making this an identity map.
172155 Future <Map <String , String >> get moduleToModulePath async {
173156 await _initialize ();
174157 return _moduleToModulePath;
@@ -182,67 +165,108 @@ class MetadataProvider {
182165 /// web/main,
183166 /// web/foo/bar
184167 /// ]
185- ///
186- /// If [_useModuleName] is false, this will be the set of module paths
187- /// instead.
188168 Future <List <String >> get modules async {
189169 await _initialize ();
190170 return _moduleToModulePath.keys.toList ();
191171 }
192172
193- Future <void > _initialize () async {
194- await _metadataMemoizer.runOnce (() async {
195- // The merged metadata resides next to the entrypoint.
196- // Assume that <name>.bootstrap.js has <name>.ddc_merged_metadata
197- if (entrypoint.endsWith ('.bootstrap.js' )) {
198- _logger.info ('Loading debug metadata...' );
199- final serverPath = entrypoint.replaceAll (
200- '.bootstrap.js' ,
201- '.ddc_merged_metadata' ,
202- );
203- final merged = await _assetReader.metadataContents (serverPath);
204- if (merged != null ) {
205- _addSdkMetadata ();
206- for (final contents in merged.split ('\n ' )) {
207- try {
208- if (contents.isEmpty ||
209- contents.startsWith ('// intentionally empty:' )) {
210- continue ;
211- }
212- final moduleJson = json.decode (contents);
213- final metadata = ModuleMetadata .fromJson (
214- moduleJson as Map <String , dynamic >,
215- );
173+ Future <Map <String , ModuleMetadata >?> _processMetadata (bool hotReload) async {
174+ final modules = hotReload ? < String , ModuleMetadata > {} : null ;
175+ // The merged metadata resides next to the entrypoint.
176+ // Assume that <name>.bootstrap.js has <name>.ddc_merged_metadata
177+ if (entrypoint.endsWith ('.bootstrap.js' )) {
178+ _logger.info ('Loading debug metadata...' );
179+ final serverPath = entrypoint.replaceAll (
180+ '.bootstrap.js' ,
181+ '.ddc_merged_metadata' ,
182+ );
183+ final merged = await _assetReader.metadataContents (serverPath);
184+ if (merged != null ) {
185+ // We can't hot reload the SDK yet, so no need to invalidate this data.
186+ if (! hotReload) _addSdkMetadata ();
187+ for (final contents in merged.split ('\n ' )) {
188+ try {
189+ if (contents.isEmpty ||
190+ contents.startsWith ('// intentionally empty:' )) {
191+ continue ;
192+ }
193+ final moduleJson = json.decode (contents);
194+ final metadata = ModuleMetadata .fromJson (
195+ moduleJson as Map <String , dynamic >,
196+ );
197+ final moduleName = metadata.name;
198+ if (hotReload) {
199+ modules? [moduleName] = metadata;
200+ } else {
216201 _addMetadata (metadata);
217- final moduleName =
218- _useModuleName ? metadata.name : metadata.moduleUri;
219- _logger.fine ('Loaded debug metadata for module: $moduleName ' );
220- } catch (e) {
221- _logger.warning ('Failed to read metadata: $e ' );
222- rethrow ;
223202 }
203+ _logger.fine ('Loaded debug metadata for module: $moduleName ' );
204+ } catch (e) {
205+ _logger.warning ('Failed to read metadata: $e ' );
206+ rethrow ;
224207 }
225208 }
226209 }
227- });
210+ }
211+ return modules;
212+ }
213+
214+ Future <void > _initialize () async {
215+ await _metadataMemoizer.runOnce (() => _processMetadata (false ));
216+ }
217+
218+ Future <void > reinitializeAfterReload (Set <String > reloadedModules) async {
219+ final modules = (await _processMetadata (true ))! ;
220+ final invalidatedLibraries = < String > {};
221+ void invalidateLibrary (String libraryImportUri) {
222+ invalidatedLibraries.add (libraryImportUri);
223+ _libraries.remove (libraryImportUri);
224+ _scriptToModule.remove (libraryImportUri);
225+ _scripts[libraryImportUri]? .forEach (_scriptToModule.remove);
226+ _scripts.remove (libraryImportUri);
227+ }
228+
229+ final deletedModules = < String > {};
230+ final invalidatedModules = < String > {};
231+ for (final module in _moduleToLibraries.keys) {
232+ final deletedModule = ! modules.containsKey (module);
233+ final invalidatedModule = reloadedModules.contains (module);
234+ assert (! (deletedModule && invalidatedModule));
235+ // If the module was either deleted or reloaded, invalidate all previous
236+ // information both about the module and its libraries.
237+ if (deletedModule || invalidatedModule) {
238+ _modulePathToModule.remove (module);
239+ _moduleToLibraries[module]? .forEach (invalidateLibrary);
240+ _moduleToModulePath.remove (module);
241+ _moduleToSourceMap.remove (module);
242+ }
243+ if (deletedModule) deletedModules.add (module);
244+ }
245+ for (final module in reloadedModules) {
246+ _addMetadata (modules[module]! );
247+ }
248+ // The libraries that were removed from the program or those that we
249+ // invalidated but were never added again.
250+ final deletedLibraries = invalidatedLibraries.where (
251+ (library) => ! _libraries.contains (library),
252+ );
228253 }
229254
230255 void _addMetadata (ModuleMetadata metadata) {
231256 final modulePath = stripLeadingSlashes (metadata.moduleUri);
232257 final sourceMapPath = stripLeadingSlashes (metadata.sourceMapUri);
233- // DDC library bundle module format does not provide names for library
234- // bundles, and therefore we use the URI instead to represent a library
235- // bundle.
236- final moduleName = _useModuleName ? metadata.name : modulePath;
258+ final moduleName = metadata.name;
237259
238260 _moduleToSourceMap[moduleName] = sourceMapPath;
239261 _modulePathToModule[modulePath] = moduleName;
240262 _moduleToModulePath[moduleName] = modulePath;
241263
264+ final moduleLibraries = < String > {};
242265 for (final library in metadata.libraries.values) {
243266 if (library.importUri.startsWith ('file:/' )) {
244267 throw AbsoluteImportUriException (library.importUri);
245268 }
269+ moduleLibraries.add (library.importUri);
246270 _libraries.add (library.importUri);
247271 _scripts[library.importUri] = [];
248272
@@ -254,6 +278,7 @@ class MetadataProvider {
254278 _scriptToModule[partPath] = moduleName;
255279 }
256280 }
281+ _moduleToLibraries[moduleName] = moduleLibraries;
257282 }
258283
259284 void _addSdkMetadata () {
@@ -264,7 +289,8 @@ class MetadataProvider {
264289 _scripts[lib] = [];
265290 // TODO(srujzs): It feels weird that we add this mapping to only this map
266291 // and not any of the other module maps. We should maybe handle this
267- // differently.
292+ // differently. This will become relevant if we ever support hot reload
293+ // for the Dart SDK.
268294 _scriptToModule[lib] = moduleName;
269295 }
270296 }
0 commit comments