@@ -15,15 +15,14 @@ class MetadataProvider {
15
15
final AssetReader _assetReader;
16
16
final _logger = Logger ('MetadataProvider' );
17
17
final String entrypoint;
18
- final List <String > _libraries = [] ;
18
+ final Set <String > _libraries = {} ;
19
19
final Map <String , String > _scriptToModule = {};
20
20
final Map <String , String > _moduleToSourceMap = {};
21
21
final Map <String , String > _modulePathToModule = {};
22
22
final Map <String , String > _moduleToModulePath = {};
23
+ final Map <String , Set <String >> _moduleToLibraries = {};
23
24
final Map <String , List <String >> _scripts = {};
24
25
final _metadataMemoizer = AsyncMemoizer ();
25
- // Whether to use the `name` provided in the module metadata.
26
- final bool _useModuleName;
27
26
28
27
/// Implicitly imported libraries in any DDC component.
29
28
///
@@ -66,11 +65,7 @@ class MetadataProvider {
66
65
'dart:ui' ,
67
66
];
68
67
69
- MetadataProvider (
70
- this .entrypoint,
71
- this ._assetReader, {
72
- required bool useModuleName,
73
- }) : _useModuleName = useModuleName;
68
+ MetadataProvider (this .entrypoint, this ._assetReader);
74
69
75
70
/// A sound null safety mode for the whole app.
76
71
///
@@ -81,17 +76,17 @@ class MetadataProvider {
81
76
return true ;
82
77
}
83
78
84
- /// A list of all libraries in the Dart application.
79
+ /// A set of all libraries in the Dart application.
85
80
///
86
81
/// Example:
87
82
///
88
- /// [
83
+ /// {
89
84
/// dart:web_gl,
90
85
/// dart:math,
91
86
/// org-dartlang-app:///web/main.dart
92
- /// ]
87
+ /// }
93
88
///
94
- Future <List <String >> get libraries async {
89
+ Future <Set <String >> get libraries async {
95
90
await _initialize ();
96
91
return _libraries;
97
92
}
@@ -118,9 +113,6 @@ class MetadataProvider {
118
113
/// org-dartlang-app:///web/main.dart :
119
114
/// web/main
120
115
/// }
121
- ///
122
- /// If [_useModuleName] is false, the values will be the module paths instead
123
- /// of the name except for 'dart_sdk'.
124
116
Future <Map <String , String >> get scriptToModule async {
125
117
await _initialize ();
126
118
return _scriptToModule;
@@ -134,9 +126,6 @@ class MetadataProvider {
134
126
/// org-dartlang-app:///web/main.dart :
135
127
/// web/main.ddc.js.map
136
128
/// }
137
- ///
138
- /// If [_useModuleName] is false, the keys will be the module paths instead of
139
- /// the name.
140
129
Future <Map <String , String >> get moduleToSourceMap async {
141
130
await _initialize ();
142
131
return _moduleToSourceMap;
@@ -150,9 +139,6 @@ class MetadataProvider {
150
139
/// web/main.ddc.js :
151
140
/// web/main
152
141
/// }
153
- ///
154
- /// If [_useModuleName] is false, the values will be the module paths instead
155
- /// of the name, making this an identity map.
156
142
Future <Map <String , String >> get modulePathToModule async {
157
143
await _initialize ();
158
144
return _modulePathToModule;
@@ -166,9 +152,6 @@ class MetadataProvider {
166
152
/// web/main
167
153
/// web/main.ddc.js :
168
154
/// }
169
- ///
170
- /// If [_useModuleName] is false, the keys will be the module paths instead of
171
- /// the name, making this an identity map.
172
155
Future <Map <String , String >> get moduleToModulePath async {
173
156
await _initialize ();
174
157
return _moduleToModulePath;
@@ -182,67 +165,108 @@ class MetadataProvider {
182
165
/// web/main,
183
166
/// web/foo/bar
184
167
/// ]
185
- ///
186
- /// If [_useModuleName] is false, this will be the set of module paths
187
- /// instead.
188
168
Future <List <String >> get modules async {
189
169
await _initialize ();
190
170
return _moduleToModulePath.keys.toList ();
191
171
}
192
172
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 {
216
201
_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 ;
223
202
}
203
+ _logger.fine ('Loaded debug metadata for module: $moduleName ' );
204
+ } catch (e) {
205
+ _logger.warning ('Failed to read metadata: $e ' );
206
+ rethrow ;
224
207
}
225
208
}
226
209
}
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
+ );
228
253
}
229
254
230
255
void _addMetadata (ModuleMetadata metadata) {
231
256
final modulePath = stripLeadingSlashes (metadata.moduleUri);
232
257
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;
237
259
238
260
_moduleToSourceMap[moduleName] = sourceMapPath;
239
261
_modulePathToModule[modulePath] = moduleName;
240
262
_moduleToModulePath[moduleName] = modulePath;
241
263
264
+ final moduleLibraries = < String > {};
242
265
for (final library in metadata.libraries.values) {
243
266
if (library.importUri.startsWith ('file:/' )) {
244
267
throw AbsoluteImportUriException (library.importUri);
245
268
}
269
+ moduleLibraries.add (library.importUri);
246
270
_libraries.add (library.importUri);
247
271
_scripts[library.importUri] = [];
248
272
@@ -254,6 +278,7 @@ class MetadataProvider {
254
278
_scriptToModule[partPath] = moduleName;
255
279
}
256
280
}
281
+ _moduleToLibraries[moduleName] = moduleLibraries;
257
282
}
258
283
259
284
void _addSdkMetadata () {
@@ -264,7 +289,8 @@ class MetadataProvider {
264
289
_scripts[lib] = [];
265
290
// TODO(srujzs): It feels weird that we add this mapping to only this map
266
291
// 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.
268
294
_scriptToModule[lib] = moduleName;
269
295
}
270
296
}
0 commit comments