diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index bdc733b8e..17f414c11 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,6 +1,7 @@ -## 24.4.0-wip +## 24.4.0 - Added support for breakpoint registering on a hot reload with the DDC library bundle format using PausePostRequests. +- `FrontendServerDdcLibraryBundleStrategy.hotReloadSourceUri` is now expected to also provide the reloaded modules. ## 24.3.11 diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart index 2a638dae7..c96c2bdbe 100644 --- a/dwds/lib/src/debugging/debugger.dart +++ b/dwds/lib/src/debugging/debugger.dart @@ -594,6 +594,7 @@ class Debugger extends Domain { params: { 'skipList': _skipLists.compute( scriptId, + url, await _locations.locationsForUrl(url), ), }, diff --git a/dwds/lib/src/debugging/inspector.dart b/dwds/lib/src/debugging/inspector.dart index 1e4932e38..25cf1b61a 100644 --- a/dwds/lib/src/debugging/inspector.dart +++ b/dwds/lib/src/debugging/inspector.dart @@ -13,6 +13,7 @@ import 'package:dwds/src/debugging/execution_context.dart'; import 'package:dwds/src/debugging/instance.dart'; import 'package:dwds/src/debugging/libraries.dart'; import 'package:dwds/src/debugging/location.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/debugging/remote_debugger.dart'; import 'package:dwds/src/loaders/ddc_library_bundle.dart'; import 'package:dwds/src/readers/asset_reader.dart'; @@ -34,7 +35,9 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; class AppInspector implements AppInspectorInterface { var _scriptCacheMemoizer = AsyncMemoizer>(); - Future> get scriptRefs => _populateScriptCaches(); + Future> getScriptRefs({ + ModifiedModuleReport? modifiedModuleReport, + }) => _populateScriptCaches(modifiedModuleReport: modifiedModuleReport); final _logger = Logger('AppInspector'); @@ -103,24 +106,35 @@ class AppInspector implements AppInspectorInterface { /// Reset all caches and recompute any mappings. /// - /// Should be called across hot reloads. - Future initialize() async { + /// Should be called across hot reloads with a valid [ModifiedModuleReport]. + Future initialize({ModifiedModuleReport? modifiedModuleReport}) async { _scriptCacheMemoizer = AsyncMemoizer>(); - _scriptRefsById.clear(); - _serverPathToScriptRef.clear(); - _scriptIdToLibraryId.clear(); - _libraryIdToScriptRefs.clear(); - _libraryHelper = LibraryHelper(this); + // TODO(srujzs): We can invalidate these in a smarter way instead of + // reinitializing when doing a hot reload, but these helpers recompute info + // on demand later and therefore are not in the critical path. _classHelper = ClassHelper(this); _instanceHelper = InstanceHelper(this); + if (modifiedModuleReport != null) { + // Invalidate `_libraryHelper` as we use it populate any script caches. + _libraryHelper.initialize(modifiedModuleReport: modifiedModuleReport); + } else { + _libraryHelper = LibraryHelper(this)..initialize(); + _scriptRefsById.clear(); + _serverPathToScriptRef.clear(); + _scriptIdToLibraryId.clear(); + _libraryIdToScriptRefs.clear(); + } + final libraries = await _libraryHelper.libraryRefs; isolate.rootLib = await _libraryHelper.rootLib; isolate.libraries?.clear(); isolate.libraries?.addAll(libraries); - final scripts = await scriptRefs; + final scripts = await getScriptRefs( + modifiedModuleReport: modifiedModuleReport, + ); await DartUri.initialize(); DartUri.recordAbsoluteUris(libraries.map((lib) => lib.uri).nonNulls); @@ -583,7 +597,7 @@ class AppInspector implements AppInspectorInterface { /// All the scripts in the isolate. @override Future getScripts() async { - return ScriptList(scripts: await scriptRefs); + return ScriptList(scripts: await getScriptRefs()); } /// Calls the Chrome Runtime.getProperties API for the object with [objectId]. @@ -714,19 +728,50 @@ class AppInspector implements AppInspectorInterface { /// /// This will get repopulated on restarts and reloads. /// + /// If [modifiedModuleReport] is provided, only invalidates and + /// recalculates caches for the modified libraries. + /// /// Returns the list of scripts refs cached. - Future> _populateScriptCaches() { + Future> _populateScriptCaches({ + ModifiedModuleReport? modifiedModuleReport, + }) { return _scriptCacheMemoizer.runOnce(() async { final scripts = await globalToolConfiguration.loadStrategy .metadataProviderFor(appConnection.request.entrypointPath) .scripts; + if (modifiedModuleReport != null) { + // Invalidate any script caches that were computed for the now invalid + // libraries. They will get repopulated below. + for (final libraryUri in modifiedModuleReport.modifiedLibraries) { + final libraryRef = await _libraryHelper.libraryRefFor(libraryUri); + final libraryId = libraryRef?.id; + // If this was not a pre-existing library, nothing to invalidate. + if (libraryId == null) continue; + final scriptRefs = _libraryIdToScriptRefs.remove(libraryId); + if (scriptRefs == null) continue; + for (final scriptRef in scriptRefs) { + final scriptId = scriptRef.id; + final scriptUri = scriptRef.uri; + if (scriptId != null && scriptUri != null) { + _scriptRefsById.remove(scriptId); + _scriptIdToLibraryId.remove(scriptId); + _serverPathToScriptRef.remove( + DartUri(scriptUri, _root).serverPath, + ); + } + } + } + } // For all the non-dart: libraries, find their parts and create scriptRefs // for them. final userLibraries = _userLibraryUris( isolate.libraries ?? [], ); for (final uri in userLibraries) { + if (modifiedModuleReport?.modifiedLibraries.contains(uri) == false) { + continue; + } final parts = scripts[uri]; final scriptRefs = [ ScriptRef(uri: uri, id: createId()), diff --git a/dwds/lib/src/debugging/libraries.dart b/dwds/lib/src/debugging/libraries.dart index e97234bed..1dc07a4c8 100644 --- a/dwds/lib/src/debugging/libraries.dart +++ b/dwds/lib/src/debugging/libraries.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:dwds/src/config/tool_configuration.dart'; import 'package:dwds/src/debugging/metadata/class.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/services/chrome_debug_exception.dart'; import 'package:dwds/src/utilities/domain.dart'; import 'package:logging/logging.dart'; @@ -27,6 +28,29 @@ class LibraryHelper extends Domain { inspector = appInspector; } + /// Initialize any caches. + /// + /// If [modifiedModuleReport] is not null, invalidates only modified libraries + /// from the cache and recomputes values for any eager caches. + void initialize({ModifiedModuleReport? modifiedModuleReport}) { + _rootLib = null; + if (modifiedModuleReport != null) { + for (final library in modifiedModuleReport.modifiedLibraries) { + // These will later be initialized by `libraryFor` if needed. + _librariesById.remove(library); + _libraryRefsById.remove(library); + } + for (final library in modifiedModuleReport.reloadedLibraries) { + // These need to be recomputed here as `libraryRefs` only checks if this + // map is empty before returning. + _libraryRefsById[library] = _createLibraryRef(library); + } + return; + } + _librariesById.clear(); + _libraryRefsById.clear(); + } + Future get rootLib async { if (_rootLib != null) return _rootLib!; final libraries = await libraryRefs; @@ -51,9 +75,13 @@ class LibraryHelper extends Domain { return _rootLib!; } + LibraryRef _createLibraryRef(String library) => + LibraryRef(id: library, name: library, uri: library); + /// Returns all libraryRefs in the app. /// - /// Note this can return a cached result. + /// Note this can return a cached result that can be selectively reinitialized + /// using [initialize]. Future> get libraryRefs async { if (_libraryRefsById.isNotEmpty) return _libraryRefsById.values.toList(); final libraries = @@ -61,11 +89,7 @@ class LibraryHelper extends Domain { .metadataProviderFor(inspector.appConnection.request.entrypointPath) .libraries; for (final library in libraries) { - _libraryRefsById[library] = LibraryRef( - id: library, - name: library, - uri: library, - ); + _libraryRefsById[library] = _createLibraryRef(library); } return _libraryRefsById.values.toList(); } diff --git a/dwds/lib/src/debugging/location.dart b/dwds/lib/src/debugging/location.dart index f4640fe2a..d983f280f 100644 --- a/dwds/lib/src/debugging/location.dart +++ b/dwds/lib/src/debugging/location.dart @@ -4,6 +4,7 @@ import 'package:async/async.dart'; import 'package:dwds/src/config/tool_configuration.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/debugging/modules.dart'; import 'package:dwds/src/readers/asset_reader.dart'; import 'package:dwds/src/utilities/dart_uri.dart'; @@ -151,11 +152,34 @@ class Locations { Modules get modules => _modules; - void initialize(String entrypoint) { - _sourceToTokenPosTable.clear(); - _sourceToLocation.clear(); + /// Initialize any caches. + /// + /// If [modifiedModuleReport] is not null, only invalidates the caches for the + /// modified modules instead. + Future initialize( + String entrypoint, { + ModifiedModuleReport? modifiedModuleReport, + }) async { + // If we know that only certain modules are deleted or added, we can only + // invalidate those. + if (modifiedModuleReport != null) { + for (final module in modifiedModuleReport.modifiedModules) { + _locationMemoizer.remove(module); + _moduleToLocations.remove(module); + final sources = await _modules.sourcesForModule(module); + if (sources != null) { + for (final serverPath in sources) { + _sourceToTokenPosTable.remove(serverPath); + _sourceToLocation.remove(serverPath); + } + } + } + return; + } _locationMemoizer.clear(); _moduleToLocations.clear(); + _sourceToTokenPosTable.clear(); + _sourceToLocation.clear(); _entrypoint = entrypoint; } diff --git a/dwds/lib/src/debugging/metadata/provider.dart b/dwds/lib/src/debugging/metadata/provider.dart index 1c1b9e0ee..86fb5e730 100644 --- a/dwds/lib/src/debugging/metadata/provider.dart +++ b/dwds/lib/src/debugging/metadata/provider.dart @@ -15,15 +15,14 @@ class MetadataProvider { final AssetReader _assetReader; final _logger = Logger('MetadataProvider'); final String entrypoint; - final List _libraries = []; + final Set _libraries = {}; final Map _scriptToModule = {}; final Map _moduleToSourceMap = {}; final Map _modulePathToModule = {}; final Map _moduleToModulePath = {}; + final Map> _moduleToLibraries = {}; final Map> _scripts = {}; final _metadataMemoizer = AsyncMemoizer(); - // Whether to use the `name` provided in the module metadata. - final bool _useModuleName; /// Implicitly imported libraries in any DDC component. /// @@ -66,11 +65,7 @@ class MetadataProvider { 'dart:ui', ]; - MetadataProvider( - this.entrypoint, - this._assetReader, { - required bool useModuleName, - }) : _useModuleName = useModuleName; + MetadataProvider(this.entrypoint, this._assetReader); /// A sound null safety mode for the whole app. /// @@ -93,7 +88,7 @@ class MetadataProvider { /// Future> get libraries async { await _initialize(); - return _libraries; + return _libraries.toList(); } /// A map of library uri to dart scripts. @@ -118,9 +113,6 @@ class MetadataProvider { /// org-dartlang-app:///web/main.dart : /// web/main /// } - /// - /// If [_useModuleName] is false, the values will be the module paths instead - /// of the name except for 'dart_sdk'. Future> get scriptToModule async { await _initialize(); return _scriptToModule; @@ -134,9 +126,6 @@ class MetadataProvider { /// org-dartlang-app:///web/main.dart : /// web/main.ddc.js.map /// } - /// - /// If [_useModuleName] is false, the keys will be the module paths instead of - /// the name. Future> get moduleToSourceMap async { await _initialize(); return _moduleToSourceMap; @@ -150,9 +139,6 @@ class MetadataProvider { /// web/main.ddc.js : /// web/main /// } - /// - /// If [_useModuleName] is false, the values will be the module paths instead - /// of the name, making this an identity map. Future> get modulePathToModule async { await _initialize(); return _modulePathToModule; @@ -166,9 +152,6 @@ class MetadataProvider { /// web/main /// web/main.ddc.js : /// } - /// - /// If [_useModuleName] is false, the keys will be the module paths instead of - /// the name, making this an identity map. Future> get moduleToModulePath async { await _initialize(); return _moduleToModulePath; @@ -182,67 +165,129 @@ class MetadataProvider { /// web/main, /// web/foo/bar /// ] - /// - /// If [_useModuleName] is false, this will be the set of module paths - /// instead. Future> get modules async { await _initialize(); return _moduleToModulePath.keys.toList(); } - Future _initialize() async { - await _metadataMemoizer.runOnce(() async { - // The merged metadata resides next to the entrypoint. - // Assume that .bootstrap.js has .ddc_merged_metadata - if (entrypoint.endsWith('.bootstrap.js')) { - _logger.info('Loading debug metadata...'); - final serverPath = entrypoint.replaceAll( - '.bootstrap.js', - '.ddc_merged_metadata', - ); - final merged = await _assetReader.metadataContents(serverPath); - if (merged != null) { - _addSdkMetadata(); - for (final contents in merged.split('\n')) { - try { - if (contents.isEmpty || - contents.startsWith('// intentionally empty:')) { - continue; - } - final moduleJson = json.decode(contents); - final metadata = ModuleMetadata.fromJson( - moduleJson as Map, - ); - _addMetadata(metadata); - final moduleName = - _useModuleName ? metadata.name : metadata.moduleUri; - _logger.fine('Loaded debug metadata for module: $moduleName'); - } catch (e) { - _logger.warning('Failed to read metadata: $e'); - rethrow; + /// Compute metadata information after reading the metadata contents and + /// return a map from module names to their [ModuleMetadata]. + Future> _processMetadata() async { + final modules = {}; + // The merged metadata resides next to the entrypoint. + // Assume that .bootstrap.js has .ddc_merged_metadata + if (entrypoint.endsWith('.bootstrap.js')) { + _logger.info('Loading debug metadata...'); + final serverPath = entrypoint.replaceAll( + '.bootstrap.js', + '.ddc_merged_metadata', + ); + final merged = await _assetReader.metadataContents(serverPath); + if (merged != null) { + for (final contents in merged.split('\n')) { + try { + if (contents.isEmpty || + contents.startsWith('// intentionally empty:')) { + continue; } + final moduleJson = json.decode(contents); + final metadata = ModuleMetadata.fromJson( + moduleJson as Map, + ); + final moduleName = metadata.name; + modules[moduleName] = metadata; + _logger.fine('Loaded debug metadata for module: $moduleName'); + } catch (e) { + _logger.warning('Failed to read metadata: $e'); + rethrow; } } } + } + return modules; + } + + /// Process all metadata, including SDK metadata, and compute caches once. + Future _initialize() async { + await _metadataMemoizer.runOnce(() async { + final metadata = await _processMetadata(); + _addSdkMetadata(); + metadata.values.forEach(_addMetadata); }); } + /// Given a map of hot reloaded modules mapped to their respective libraries, + /// determines deleted and invalidated libraries and modules, invalidates them + /// in any caches, and recomputes the necessary information. + /// + /// Returns a [ModifiedModuleReport] that can be used to invalidate other + /// caches after a hot reload. + Future reinitializeAfterHotReload( + Map reloadedModulesToLibraries, + ) async { + final modules = await _processMetadata(); + final invalidatedLibraries = {}; + void invalidateLibrary(String libraryImportUri) { + invalidatedLibraries.add(libraryImportUri); + _libraries.remove(libraryImportUri); + _scriptToModule.remove(libraryImportUri); + _scripts[libraryImportUri]?.forEach(_scriptToModule.remove); + _scripts.remove(libraryImportUri); + } + + final deletedModules = {}; + for (final module in _moduleToLibraries.keys) { + final deletedModule = !modules.containsKey(module); + final invalidatedModule = reloadedModulesToLibraries.containsKey(module); + assert(!(deletedModule && invalidatedModule)); + // If the module was either deleted or reloaded, invalidate all previous + // information both about the module and its libraries. + if (deletedModule || invalidatedModule) { + _modulePathToModule.remove(module); + _moduleToLibraries[module]?.forEach(invalidateLibrary); + _moduleToModulePath.remove(module); + _moduleToSourceMap.remove(module); + } + if (deletedModule) deletedModules.add(module); + } + final reloadedModules = {}; + final reloadedLibraries = {}; + for (final module in reloadedModulesToLibraries.keys) { + reloadedModules.add(module); + reloadedLibraries.addAll( + reloadedModulesToLibraries[module]!.cast(), + ); + _addMetadata(modules[module]!); + } + // The libraries that are removed from the program are those that we + // invalidated but were never added again. + final deletedLibraries = + invalidatedLibraries + .where((library) => !_libraries.contains(library)) + .toSet(); + return ModifiedModuleReport( + deletedModules: deletedModules, + deletedLibraries: deletedLibraries, + reloadedModules: reloadedModules, + reloadedLibraries: reloadedLibraries, + ); + } + void _addMetadata(ModuleMetadata metadata) { final modulePath = stripLeadingSlashes(metadata.moduleUri); final sourceMapPath = stripLeadingSlashes(metadata.sourceMapUri); - // DDC library bundle module format does not provide names for library - // bundles, and therefore we use the URI instead to represent a library - // bundle. - final moduleName = _useModuleName ? metadata.name : modulePath; + final moduleName = metadata.name; _moduleToSourceMap[moduleName] = sourceMapPath; _modulePathToModule[modulePath] = moduleName; _moduleToModulePath[moduleName] = modulePath; + final moduleLibraries = {}; for (final library in metadata.libraries.values) { if (library.importUri.startsWith('file:/')) { throw AbsoluteImportUriException(library.importUri); } + moduleLibraries.add(library.importUri); _libraries.add(library.importUri); _scripts[library.importUri] = []; @@ -254,6 +299,7 @@ class MetadataProvider { _scriptToModule[partPath] = moduleName; } } + _moduleToLibraries[moduleName] = moduleLibraries; } void _addSdkMetadata() { @@ -264,7 +310,8 @@ class MetadataProvider { _scripts[lib] = []; // TODO(srujzs): It feels weird that we add this mapping to only this map // and not any of the other module maps. We should maybe handle this - // differently. + // differently. This will become relevant if we ever support hot reload + // for the Dart SDK. _scriptToModule[lib] = moduleName; } } @@ -277,3 +324,36 @@ class AbsoluteImportUriException implements Exception { @override String toString() => "AbsoluteImportUriError: '$importUri'"; } + +/// Computed after a hot reload using +/// [MetadataProvider.reinitializeAfterHotReload], represents the modules and +/// libraries in the program that were deleted, reloaded, and therefore, +/// modified. +/// +/// Used to recompute caches throughout DWDS. +class ModifiedModuleReport { + /// Module names that are no longer in the program. + final Set deletedModules; + + /// Library uris that are no longer in the program. + final Set deletedLibraries; + + /// Module names that were loaded during the hot reload. + final Set reloadedModules; + + /// Library uris that were loaded during the hot reload. + final Set reloadedLibraries; + + /// Module names that were either removed or modified, including additions. + final Set modifiedModules; + + /// Library uris that were either removed or modified, including additions. + final Set modifiedLibraries; + ModifiedModuleReport({ + required this.deletedModules, + required this.deletedLibraries, + required this.reloadedModules, + required this.reloadedLibraries, + }) : modifiedModules = deletedModules.union(reloadedModules), + modifiedLibraries = deletedLibraries.union(reloadedLibraries); +} diff --git a/dwds/lib/src/debugging/modules.dart b/dwds/lib/src/debugging/modules.dart index ea4dd1117..15f10b06a 100644 --- a/dwds/lib/src/debugging/modules.dart +++ b/dwds/lib/src/debugging/modules.dart @@ -5,6 +5,7 @@ import 'package:async/async.dart'; import 'package:dwds/src/config/tool_configuration.dart'; import 'package:dwds/src/debugging/debugger.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/utilities/dart_uri.dart'; import 'package:logging/logging.dart'; @@ -16,6 +17,9 @@ class Modules { // The Dart server path to containing module. final _sourceToModule = {}; + // Module to Dart server paths. + final _moduleToSources = >{}; + // The Dart server path to library import uri final _sourceToLibrary = {}; var _moduleMemoizer = AsyncMemoizer(); @@ -26,18 +30,37 @@ class Modules { Modules(this._root); - /// Initializes the mapping from source to module. + /// Initializes mappings after invalidating modified libraries/modules. /// /// Intended to be called multiple times throughout the development workflow, /// e.g. after a hot-reload. - void initialize(String entrypoint) { - // We only clear the source to module mapping as script IDs may persist - // across hot reloads. - _sourceToModule.clear(); + /// + /// If [modifiedModuleReport] is not null, removes and recalculates caches for + /// any modified modules and libraries. + Future initialize( + String entrypoint, { + ModifiedModuleReport? modifiedModuleReport, + }) async { + if (modifiedModuleReport != null) { + assert(_entrypoint == entrypoint); + for (final library in modifiedModuleReport.modifiedLibraries) { + final libraryServerPath = _getLibraryServerPath(library); + _sourceToLibrary.remove(libraryServerPath); + _sourceToModule.remove(libraryServerPath); + _libraryToModule.remove(library); + } + for (final module in modifiedModuleReport.modifiedModules) { + _moduleToSources.remove(module); + } + await _initializeMapping(modifiedModuleReport); + return; + } + _entrypoint = entrypoint; _sourceToLibrary.clear(); + _sourceToModule.clear(); _libraryToModule.clear(); + _moduleToSources.clear(); _moduleMemoizer = AsyncMemoizer(); - _entrypoint = entrypoint; } /// Returns the containing module for the provided Dart server path. @@ -46,6 +69,12 @@ class Modules { return _sourceToModule[serverPath]; } + /// Returns the Dart server paths for the provided module. + Future?> sourcesForModule(String module) async { + await _moduleMemoizer.runOnce(_initializeMapping); + return _moduleToSources[module]; + } + /// Returns the containing library importUri for the provided Dart server path. Future libraryForSource(String serverPath) async { await _moduleMemoizer.runOnce(_initializeMapping); @@ -69,11 +98,24 @@ class Modules { ) async { final serverPath = await globalToolConfiguration.loadStrategy .serverPathForModule(entrypoint, module); + // TODO(srujzs): We should wait until all scripts are parsed before + // accessing after a hot reload. See + // https://github.com/dart-lang/webdev/issues/2640. return chromePathToRuntimeScriptId[serverPath]; } - /// Initializes [_sourceToModule] and [_sourceToLibrary]. - Future _initializeMapping() async { + String _getLibraryServerPath(String library) => + library.startsWith('dart:') + ? library + : DartUri(library, _root).serverPath; + + /// Initializes [_sourceToModule], [_moduleToSources], and [_sourceToLibrary]. + /// + /// If [modifiedModuleReport] is not null, only updates the maps for the + /// modified libraries in the report. + Future _initializeMapping([ + ModifiedModuleReport? modifiedModuleReport, + ]) async { final provider = globalToolConfiguration.loadStrategy.metadataProviderFor( _entrypoint, ); @@ -82,26 +124,27 @@ class Modules { final scriptToModule = await provider.scriptToModule; for (final library in libraryToScripts.keys) { + if (modifiedModuleReport?.modifiedLibraries.contains(library) == false) { + // Note that every module will have at least one library associated with + // it, so it's okay to only process the modified libraries. + continue; + } final scripts = libraryToScripts[library]!; - final libraryServerPath = - library.startsWith('dart:') - ? library - : DartUri(library, _root).serverPath; + final libraryServerPath = _getLibraryServerPath(library); if (scriptToModule.containsKey(library)) { final module = scriptToModule[library]!; _sourceToModule[libraryServerPath] = module; + _moduleToSources.putIfAbsent(module, () => {}).add(libraryServerPath); _sourceToLibrary[libraryServerPath] = Uri.parse(library); _libraryToModule[library] = module; for (final script in scripts) { - final scriptServerPath = - script.startsWith('dart:') - ? script - : DartUri(script, _root).serverPath; + final scriptServerPath = _getLibraryServerPath(script); _sourceToModule[scriptServerPath] = module; + _moduleToSources[module]!.add(scriptServerPath); _sourceToLibrary[scriptServerPath] = Uri.parse(library); } } else { diff --git a/dwds/lib/src/debugging/skip_list.dart b/dwds/lib/src/debugging/skip_list.dart index d2b0c817d..7b5f94291 100644 --- a/dwds/lib/src/debugging/skip_list.dart +++ b/dwds/lib/src/debugging/skip_list.dart @@ -2,15 +2,46 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:dwds/src/config/tool_configuration.dart'; import 'package:dwds/src/debugging/location.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; +import 'package:dwds/src/utilities/dart_uri.dart'; const maxValue = 2147483647; class SkipLists { // Map of script ID to scriptList. final _idToList = >>{}; + // Map of url to script ID. + final _urlToId = {}; + final String _root; - void initialize() => _idToList.clear(); + SkipLists(this._root); + + /// Initialize any caches. + /// + /// If [modifiedModuleReport] is not null, only invalidates the caches for the + /// modified modules instead. + Future initialize( + String entrypoint, { + ModifiedModuleReport? modifiedModuleReport, + }) async { + if (modifiedModuleReport != null) { + for (final url in _urlToId.keys) { + final dartUri = DartUri(url, _root); + final serverPath = dartUri.serverPath; + final module = await globalToolConfiguration.loadStrategy + .moduleForServerPath(entrypoint, serverPath); + if (modifiedModuleReport.modifiedModules.contains(module)) { + _idToList.remove(_urlToId[url]!); + _urlToId.remove(url); + } + } + return; + } + _idToList.clear(); + _urlToId.clear(); + } /// Returns a skipList as defined by the Chrome DevTools Protocol. /// @@ -18,7 +49,11 @@ class SkipLists { /// https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepInto /// /// Can return a cached value. - List> compute(String scriptId, Set locations) { + List> compute( + String scriptId, + String url, + Set locations, + ) { if (_idToList.containsKey(scriptId)) return _idToList[scriptId]!; final sortedLocations = @@ -49,7 +84,10 @@ class SkipLists { } ranges.add(_rangeFor(scriptId, startLine, startColumn, maxValue, maxValue)); - _idToList[scriptId] = ranges; + if (url.isNotEmpty) { + _idToList[scriptId] = ranges; + _urlToId[url] = scriptId; + } return ranges; } diff --git a/dwds/lib/src/injected/client.js b/dwds/lib/src/injected/client.js index 488511c5c..e85ffd662 100644 --- a/dwds/lib/src/injected/client.js +++ b/dwds/lib/src/injected/client.js @@ -21651,6 +21651,7 @@ }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { var t1, t2, value, $$v, _$result, + _s11_ = "BuildResult", result = new A.BuildResultBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); for (t1 = type$.BuildStatus; iterator.moveNext$0();) { @@ -21674,7 +21675,13 @@ } } _$result = result._build_result$_$v; - return result._build_result$_$v = _$result == null ? new A._$BuildResult(A.BuiltValueNullFieldError_checkNotNull(result.get$_build_result$_$this()._status, "BuildResult", "status", t1)) : _$result; + if (_$result == null) { + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_build_result$_$this()._status, _s11_, "status", t1); + _$result = new A._$BuildResult(t2); + A.BuiltValueNullFieldError_checkNotNull(t2, _s11_, "status", t1); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.BuildResult); + return result._build_result$_$v = _$result; }, deserialize$2(serializers, serialized) { return this.deserialize$3$specifiedType(serializers, serialized, B.FullType_null_List_empty_false); @@ -21805,13 +21812,22 @@ return _this; }, _connect_request$_build$0() { - var t1, _this = this, + var t1, t2, t3, t4, _this = this, _s14_ = "ConnectRequest", + _s10_ = "instanceId", + _s14_0 = "entrypointPath", _$result = _this._connect_request$_$v; if (_$result == null) { t1 = type$.String; - _$result = new A._$ConnectRequest(A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._connect_request$_appId, _s14_, "appId", t1), A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._instanceId, _s14_, "instanceId", t1), A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._entrypointPath, _s14_, "entrypointPath", t1)); - } + t2 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._connect_request$_appId, _s14_, "appId", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._instanceId, _s14_, _s10_, t1); + t4 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_connect_request$_$this()._entrypointPath, _s14_, _s14_0, t1); + _$result = new A._$ConnectRequest(t2, t3, t4); + A.BuiltValueNullFieldError_checkNotNull(t2, _s14_, "appId", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s14_, _s10_, t1); + A.BuiltValueNullFieldError_checkNotNull(t4, _s14_, _s14_0, t1); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.ConnectRequest); return _this._connect_request$_$v = _$result; } }; @@ -21976,13 +21992,23 @@ return _this; }, _debug_event$_build$0() { - var t1, _this = this, + var t1, t2, t3, t4, t5, _this = this, _s10_ = "DebugEvent", + _s9_ = "eventData", + _s9_0 = "timestamp", _$result = _this._debug_event$_$v; if (_$result == null) { t1 = type$.String; - _$result = new A._$DebugEvent(A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._debug_event$_kind, _s10_, "kind", t1), A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._eventData, _s10_, "eventData", t1), A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._timestamp, _s10_, "timestamp", type$.int)); - } + t2 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._debug_event$_kind, _s10_, "kind", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._eventData, _s10_, _s9_, t1); + t4 = type$.int; + t5 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_debug_event$_$this()._timestamp, _s10_, _s9_0, t4); + _$result = new A._$DebugEvent(t2, t3, t5); + A.BuiltValueNullFieldError_checkNotNull(t2, _s10_, "kind", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s10_, _s9_, t1); + A.BuiltValueNullFieldError_checkNotNull(t5, _s10_, _s9_0, t4); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.DebugEvent); return _this._debug_event$_$v = _$result; } }; @@ -22027,10 +22053,17 @@ return _this; }, _debug_event$_build$0() { - var _$failedField, e, _$result0, exception, t1, _this = this, _$result = null; + var _$failedField, e, _$result0, t1, exception, t2, _this = this, + _s18_ = "BatchedDebugEvents", + _$result = null; try { _$result0 = _this._debug_event$_$v; - _$result = _$result0 == null ? new A._$BatchedDebugEvents(_this.get$events().build$0()) : _$result0; + if (_$result0 == null) { + t1 = _this.get$events().build$0(); + _$result0 = new A._$BatchedDebugEvents(t1); + A.BuiltValueNullFieldError_checkNotNull(t1, _s18_, "events", type$.BuiltList_DebugEvent); + } + _$result = _$result0; } catch (exception) { _$failedField = A._Cell$named("_$failedField"); try { @@ -22038,12 +22071,15 @@ _this.get$events().build$0(); } catch (exception) { e = A.unwrapException(exception); - t1 = A.BuiltValueNestedFieldError$("BatchedDebugEvents", _$failedField.readLocal$0(), J.toString$0$(e)); + t1 = A.BuiltValueNestedFieldError$(_s18_, _$failedField.readLocal$0(), J.toString$0$(e)); throw A.wrapException(t1); } throw exception; } - _this._debug_event$_$v = type$.BatchedDebugEvents._as(_$result); + t1 = type$.BatchedDebugEvents; + t2 = t1._as(_$result); + A.ArgumentError_checkNotNull(t2, "other", t1); + _this._debug_event$_$v = t2; return _$result; }, set$_events(_events) { @@ -22263,7 +22299,10 @@ _build$0() { var _this = this, _$result = _this._$v; - return _this._$v = _$result == null ? new A._$DebugInfo(_this.get$_$this()._appEntrypointPath, _this.get$_$this()._appId, _this.get$_$this()._appInstanceId, _this.get$_$this()._appOrigin, _this.get$_$this()._appUrl, _this.get$_$this()._authUrl, _this.get$_$this()._dwdsVersion, _this.get$_$this()._extensionUrl, _this.get$_$this()._isInternalBuild, _this.get$_$this()._isFlutterApp, _this.get$_$this()._workspaceName, _this.get$_$this()._tabUrl, _this.get$_$this()._tabId) : _$result; + if (_$result == null) + _$result = new A._$DebugInfo(_this.get$_$this()._appEntrypointPath, _this.get$_$this()._appId, _this.get$_$this()._appInstanceId, _this.get$_$this()._appOrigin, _this.get$_$this()._appUrl, _this.get$_$this()._authUrl, _this.get$_$this()._dwdsVersion, _this.get$_$this()._extensionUrl, _this.get$_$this()._isInternalBuild, _this.get$_$this()._isFlutterApp, _this.get$_$this()._workspaceName, _this.get$_$this()._tabUrl, _this.get$_$this()._tabId); + A.ArgumentError_checkNotNull(_$result, "other", type$.DebugInfo); + return _this._$v = _$result; } }; A.DevToolsRequest.prototype = {}; @@ -22369,7 +22408,8 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, _$result, + var t1, value, _$result, t2, t3, + _s15_ = "promptExtension", _s16_ = "DevToolsResponse", result = new A.DevToolsResponseBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); @@ -22401,8 +22441,13 @@ _$result = result._devtools_request$_$v; if (_$result == null) { t1 = type$.bool; - _$result = new A._$DevToolsResponse(A.BuiltValueNullFieldError_checkNotNull(result.get$_devtools_request$_$this()._success, _s16_, "success", t1), A.BuiltValueNullFieldError_checkNotNull(result.get$_devtools_request$_$this()._promptExtension, _s16_, "promptExtension", t1), result.get$_devtools_request$_$this()._error); + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_devtools_request$_$this()._success, _s16_, "success", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(result.get$_devtools_request$_$this()._promptExtension, _s16_, _s15_, t1); + _$result = new A._$DevToolsResponse(t2, t3, result.get$_devtools_request$_$this()._error); + A.BuiltValueNullFieldError_checkNotNull(t2, _s16_, "success", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s16_, _s15_, t1); } + A.ArgumentError_checkNotNull(_$result, "other", type$.DevToolsResponse); return result._devtools_request$_$v = _$result; }, deserialize$2(serializers, serialized) { @@ -22459,13 +22504,19 @@ return _this; }, _devtools_request$_build$0() { - var t1, _this = this, + var t1, t2, t3, _this = this, _s15_ = "DevToolsRequest", + _s10_ = "instanceId", _$result = _this._devtools_request$_$v; if (_$result == null) { t1 = type$.String; - _$result = new A._$DevToolsRequest(A.BuiltValueNullFieldError_checkNotNull(_this.get$_devtools_request$_$this()._devtools_request$_appId, _s15_, "appId", t1), A.BuiltValueNullFieldError_checkNotNull(_this.get$_devtools_request$_$this()._devtools_request$_instanceId, _s15_, "instanceId", t1), _this.get$_devtools_request$_$this()._contextId, _this.get$_devtools_request$_$this()._devtools_request$_tabUrl, _this.get$_devtools_request$_$this()._uriOnly, _this.get$_devtools_request$_$this()._devtools_request$_client); + t2 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_devtools_request$_$this()._devtools_request$_appId, _s15_, "appId", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_devtools_request$_$this()._devtools_request$_instanceId, _s15_, _s10_, t1); + _$result = new A._$DevToolsRequest(t2, t3, _this.get$_devtools_request$_$this()._contextId, _this.get$_devtools_request$_$this()._devtools_request$_tabUrl, _this.get$_devtools_request$_$this()._uriOnly, _this.get$_devtools_request$_$this()._devtools_request$_client); + A.BuiltValueNullFieldError_checkNotNull(t2, _s15_, "appId", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s15_, _s10_, t1); } + A.ArgumentError_checkNotNull(_$result, "other", type$.DevToolsRequest); return _this._devtools_request$_$v = _$result; } }; @@ -22516,7 +22567,8 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, $$v, _$result, + var t1, value, $$v, _$result, t2, t3, + _s10_ = "stackTrace", _s13_ = "ErrorResponse", result = new A.ErrorResponseBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); @@ -22556,8 +22608,13 @@ _$result = result._error_response$_$v; if (_$result == null) { t1 = type$.String; - _$result = new A._$ErrorResponse(A.BuiltValueNullFieldError_checkNotNull(result.get$_error_response$_$this()._error_response$_error, _s13_, "error", t1), A.BuiltValueNullFieldError_checkNotNull(result.get$_error_response$_$this()._error_response$_stackTrace, _s13_, "stackTrace", t1)); + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_error_response$_$this()._error_response$_error, _s13_, "error", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(result.get$_error_response$_$this()._error_response$_stackTrace, _s13_, _s10_, t1); + _$result = new A._$ErrorResponse(t2, t3); + A.BuiltValueNullFieldError_checkNotNull(t2, _s13_, "error", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s13_, _s10_, t1); } + A.ArgumentError_checkNotNull(_$result, "other", type$.ErrorResponse); return result._error_response$_$v = _$result; }, deserialize$2(serializers, serialized) { @@ -22623,7 +22680,7 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, _$result, + var t1, value, _$result, t2, t3, t4, _s16_ = "ExtensionRequest", result = new A.ExtensionRequestBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); @@ -22653,7 +22710,17 @@ } } _$result = result._extension_request$_$v; - return result._extension_request$_$v = _$result == null ? new A._$ExtensionRequest(A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._id, _s16_, "id", type$.int), A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._command, _s16_, "command", type$.String), result.get$_extension_request$_$this()._commandParams) : _$result; + if (_$result == null) { + t1 = type$.int; + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._id, _s16_, "id", t1); + t3 = type$.String; + t4 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._command, _s16_, "command", t3); + _$result = new A._$ExtensionRequest(t2, t4, result.get$_extension_request$_$this()._commandParams); + A.BuiltValueNullFieldError_checkNotNull(t2, _s16_, "id", t1); + A.BuiltValueNullFieldError_checkNotNull(t4, _s16_, "command", t3); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.ExtensionRequest); + return result._extension_request$_$v = _$result; }, deserialize$2(serializers, serialized) { return this.deserialize$3$specifiedType(serializers, serialized, B.FullType_null_List_empty_false); @@ -22683,7 +22750,7 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, _$result, + var t1, value, _$result, t2, t3, t4, t5, t6, _s17_ = "ExtensionResponse", result = new A.ExtensionResponseBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); @@ -22719,7 +22786,20 @@ } } _$result = result._extension_request$_$v; - return result._extension_request$_$v = _$result == null ? new A._$ExtensionResponse(A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._id, _s17_, "id", type$.int), A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_success, _s17_, "success", type$.bool), A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_result, _s17_, "result", type$.String), result.get$_extension_request$_$this()._extension_request$_error) : _$result; + if (_$result == null) { + t1 = type$.int; + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._id, _s17_, "id", t1); + t3 = type$.bool; + t4 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_success, _s17_, "success", t3); + t5 = type$.String; + t6 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_result, _s17_, "result", t5); + _$result = new A._$ExtensionResponse(t2, t4, t6, result.get$_extension_request$_$this()._extension_request$_error); + A.BuiltValueNullFieldError_checkNotNull(t2, _s17_, "id", t1); + A.BuiltValueNullFieldError_checkNotNull(t4, _s17_, "success", t3); + A.BuiltValueNullFieldError_checkNotNull(t6, _s17_, "result", t5); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.ExtensionResponse); + return result._extension_request$_$v = _$result; }, deserialize$2(serializers, serialized) { return this.deserialize$3$specifiedType(serializers, serialized, B.FullType_null_List_empty_false); @@ -22742,7 +22822,7 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, $$v, _$result, + var t1, value, $$v, _$result, t2, t3, _s14_ = "ExtensionEvent", result = new A.ExtensionEventBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); @@ -22782,8 +22862,13 @@ _$result = result._extension_request$_$v; if (_$result == null) { t1 = type$.String; - _$result = new A._$ExtensionEvent(A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._params, _s14_, "params", t1), A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_method, _s14_, "method", t1)); + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._params, _s14_, "params", t1); + t3 = A.BuiltValueNullFieldError_checkNotNull(result.get$_extension_request$_$this()._extension_request$_method, _s14_, "method", t1); + _$result = new A._$ExtensionEvent(t2, t3); + A.BuiltValueNullFieldError_checkNotNull(t2, _s14_, "params", t1); + A.BuiltValueNullFieldError_checkNotNull(t3, _s14_, "method", t1); } + A.ArgumentError_checkNotNull(_$result, "other", type$.ExtensionEvent); return result._extension_request$_$v = _$result; }, deserialize$2(serializers, serialized) { @@ -23019,10 +23104,17 @@ return t1; }, _extension_request$_build$0() { - var _$failedField, e, _$result0, exception, t1, _this = this, _$result = null; + var _$failedField, e, _$result0, t1, exception, t2, _this = this, + _s13_ = "BatchedEvents", + _$result = null; try { _$result0 = _this._extension_request$_$v; - _$result = _$result0 == null ? new A._$BatchedEvents(_this.get$events().build$0()) : _$result0; + if (_$result0 == null) { + t1 = _this.get$events().build$0(); + _$result0 = new A._$BatchedEvents(t1); + A.BuiltValueNullFieldError_checkNotNull(t1, _s13_, "events", type$.BuiltList_ExtensionEvent); + } + _$result = _$result0; } catch (exception) { _$failedField = A._Cell$named("_$failedField"); try { @@ -23030,12 +23122,15 @@ _this.get$events().build$0(); } catch (exception) { e = A.unwrapException(exception); - t1 = A.BuiltValueNestedFieldError$("BatchedEvents", _$failedField.readLocal$0(), J.toString$0$(e)); + t1 = A.BuiltValueNestedFieldError$(_s13_, _$failedField.readLocal$0(), J.toString$0$(e)); throw A.wrapException(t1); } throw exception; } - _this._extension_request$_$v = type$.BatchedEvents._as(_$result); + t1 = type$.BatchedEvents; + t2 = t1._as(_$result); + A.ArgumentError_checkNotNull(t2, "other", t1); + _this._extension_request$_$v = t2; return _$result; }, set$_extension_request$_events(_events) { @@ -23051,7 +23146,8 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { - var t1, value, $$v, _$result, + var t1, value, $$v, _$result, t2, + _s16_ = "HotReloadRequest", result = new A.HotReloadRequestBuilder(), iterator = J.get$iterator$ax(type$.Iterable_nullable_Object._as(serialized)); for (; iterator.moveNext$0();) { @@ -23075,7 +23171,14 @@ } } _$result = result._hot_reload_request$_$v; - return result._hot_reload_request$_$v = _$result == null ? new A._$HotReloadRequest(A.BuiltValueNullFieldError_checkNotNull(result.get$_hot_reload_request$_$this()._hot_reload_request$_id, "HotReloadRequest", "id", type$.String)) : _$result; + if (_$result == null) { + t1 = type$.String; + t2 = A.BuiltValueNullFieldError_checkNotNull(result.get$_hot_reload_request$_$this()._hot_reload_request$_id, _s16_, "id", t1); + _$result = new A._$HotReloadRequest(t2); + A.BuiltValueNullFieldError_checkNotNull(t2, _s16_, "id", t1); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.HotReloadRequest); + return result._hot_reload_request$_$v = _$result; }, deserialize$2(serializers, serialized) { return this.deserialize$3$specifiedType(serializers, serialized, B.FullType_null_List_empty_false); @@ -23223,10 +23326,20 @@ return _this; }, _hot_reload_response$_build$0() { - var _this = this, + var t1, t2, t3, t4, _this = this, _s17_ = "HotReloadResponse", _$result = _this._hot_reload_response$_$v; - return _this._hot_reload_response$_$v = _$result == null ? new A._$HotReloadResponse(A.BuiltValueNullFieldError_checkNotNull(_this.get$_hot_reload_response$_$this()._hot_reload_response$_id, _s17_, "id", type$.String), A.BuiltValueNullFieldError_checkNotNull(_this.get$_hot_reload_response$_$this()._hot_reload_response$_success, _s17_, "success", type$.bool), _this.get$_hot_reload_response$_$this()._errorMessage) : _$result; + if (_$result == null) { + t1 = type$.String; + t2 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_hot_reload_response$_$this()._hot_reload_response$_id, _s17_, "id", t1); + t3 = type$.bool; + t4 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_hot_reload_response$_$this()._hot_reload_response$_success, _s17_, "success", t3); + _$result = new A._$HotReloadResponse(t2, t4, _this.get$_hot_reload_response$_$this()._errorMessage); + A.BuiltValueNullFieldError_checkNotNull(t2, _s17_, "id", t1); + A.BuiltValueNullFieldError_checkNotNull(t4, _s17_, "success", t3); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.HotReloadResponse); + return _this._hot_reload_response$_$v = _$result; } }; A.IsolateExit.prototype = {}; @@ -23297,7 +23410,10 @@ A.IsolateExitBuilder.prototype = { _isolate_events$_build$0() { var _$result = this._isolate_events$_$v; - return this._isolate_events$_$v = _$result == null ? new A._$IsolateExit() : _$result; + if (_$result == null) + _$result = new A._$IsolateExit(); + A.ArgumentError_checkNotNull(_$result, "other", type$.IsolateExit); + return this._isolate_events$_$v = _$result; } }; A._$IsolateStart.prototype = { @@ -23318,7 +23434,10 @@ A.IsolateStartBuilder.prototype = { _isolate_events$_build$0() { var _$result = this._isolate_events$_$v; - return this._isolate_events$_$v = _$result == null ? new A._$IsolateStart() : _$result; + if (_$result == null) + _$result = new A._$IsolateStart(); + A.ArgumentError_checkNotNull(_$result, "other", type$.IsolateStart); + return this._isolate_events$_$v = _$result; } }; A.RegisterEvent.prototype = {}; @@ -23412,10 +23531,22 @@ return _this; }, _register_event$_build$0() { - var _this = this, + var t1, t2, t3, t4, _this = this, _s13_ = "RegisterEvent", + _s9_ = "eventData", + _s9_0 = "timestamp", _$result = _this._register_event$_$v; - return _this._register_event$_$v = _$result == null ? new A._$RegisterEvent(A.BuiltValueNullFieldError_checkNotNull(_this.get$_register_event$_$this()._register_event$_eventData, _s13_, "eventData", type$.String), A.BuiltValueNullFieldError_checkNotNull(_this.get$_register_event$_$this()._register_event$_timestamp, _s13_, "timestamp", type$.int)) : _$result; + if (_$result == null) { + t1 = type$.String; + t2 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_register_event$_$this()._register_event$_eventData, _s13_, _s9_, t1); + t3 = type$.int; + t4 = A.BuiltValueNullFieldError_checkNotNull(_this.get$_register_event$_$this()._register_event$_timestamp, _s13_, _s9_0, t3); + _$result = new A._$RegisterEvent(t2, t4); + A.BuiltValueNullFieldError_checkNotNull(t2, _s13_, _s9_, t1); + A.BuiltValueNullFieldError_checkNotNull(t4, _s13_, _s9_0, t3); + } + A.ArgumentError_checkNotNull(_$result, "other", type$.RegisterEvent); + return _this._register_event$_$v = _$result; } }; A.RunRequest.prototype = {}; @@ -23428,8 +23559,11 @@ return this.serialize$3$specifiedType(serializers, object, B.FullType_null_List_empty_false); }, deserialize$3$specifiedType(serializers, serialized, specifiedType) { + var _$result; type$.Iterable_nullable_Object._as(serialized); - return new A._$RunRequest(); + _$result = new A._$RunRequest(); + A.ArgumentError_checkNotNull(_$result, "other", type$.RunRequest); + return _$result; }, deserialize$2(serializers, serialized) { return this.deserialize$3$specifiedType(serializers, serialized, B.FullType_null_List_empty_false); @@ -26745,7 +26879,7 @@ }; A.main__closure.prototype = { call$0() { - return A.FutureOfJSAnyToJSPromise_get_toJS(this.manager._restarter.hotReloadStart$1(A.hotReloadSourcesPath()), type$.JSArray_nullable_Object); + return A.FutureOfJSAnyToJSPromise_get_toJS(this.manager._restarter.hotReloadStart$1(A.hotReloadSourcesPath()), type$.JSObject); }, $signature: 10 }; @@ -27162,8 +27296,8 @@ }, hotReloadStart$1(hotReloadSourcesPath) { var $async$goto = 0, - $async$completer = A._makeAsyncAwaitCompleter(type$.JSArray_nullable_Object), - $async$returnValue, $async$self = this, t4, srcLibraries, filesToLoad, librariesToReload, t5, t6, srcLibraryCast, libraries, t7, t1, t2, t3, xhr, $async$temp1, $async$temp2, $async$temp3; + $async$completer = A._makeAsyncAwaitCompleter(type$.JSObject), + $async$returnValue, $async$self = this, t4, srcModuleLibraries, filesToLoad, _this, librariesToReload, t5, t6, srcModuleLibraryCast, module, libraries, t7, t1, t2, t3, xhr, $async$temp1, $async$temp2, $async$temp3; var $async$hotReloadStart$1 = A._wrapJsFunctionForAsync(function($async$errorCode, $async$result) { if ($async$errorCode === 1) return A._asyncRethrow($async$result, $async$completer); @@ -27187,14 +27321,17 @@ return A._asyncAwait(t1, $async$hotReloadStart$1); case 3: // returning from await. - srcLibraries = $async$temp1.cast$1$0$ax($async$temp2._as($async$temp3.decode$1($async$result)), type$.Map_dynamic_dynamic); + srcModuleLibraries = $async$temp1.cast$1$0$ax($async$temp2._as($async$temp3.decode$1($async$result)), type$.Map_dynamic_dynamic); t1 = type$.JSArray_nullable_Object; filesToLoad = t1._as(new t2.Array()); + _this = {}; librariesToReload = t1._as(new t2.Array()); - for (t1 = srcLibraries.get$iterator(srcLibraries), t5 = type$.String, t6 = type$.Object; t1.moveNext$0();) { - srcLibraryCast = t1.get$current().cast$2$0(0, t5, t6); - filesToLoad.push(A._asString(srcLibraryCast.$index(0, "src"))); - libraries = J.cast$1$0$ax(t4._as(srcLibraryCast.$index(0, "libraries")), t5); + for (t1 = srcModuleLibraries.get$iterator(srcModuleLibraries), t5 = type$.String, t6 = type$.Object; t1.moveNext$0();) { + srcModuleLibraryCast = t1.get$current().cast$2$0(0, t5, t6); + filesToLoad.push(A._asString(srcModuleLibraryCast.$index(0, "src"))); + module = A._asString(srcModuleLibraryCast.$index(0, "module")); + libraries = J.cast$1$0$ax(t4._as(srcModuleLibraryCast.$index(0, "libraries")), t5); + _this[module] = A.jsify(libraries); for (t7 = libraries.get$iterator(libraries); t7.moveNext$0();) librariesToReload.push(t7.get$current()); } @@ -27203,7 +27340,7 @@ return A._asyncAwait(A.promiseToFuture(t3._as(t3._as(t2.dartDevEmbedder).hotReload(filesToLoad, librariesToReload)), type$.nullable_Object), $async$hotReloadStart$1); case 4: // returning from await. - $async$returnValue = librariesToReload; + $async$returnValue = _this; // goto return $async$goto = 1; break; @@ -28101,6 +28238,8 @@ BuildResult: findType("BuildResult"), BuildStatus: findType("BuildStatus"), BuiltListMultimap_dynamic_dynamic: findType("BuiltListMultimap<@,@>"), + BuiltList_DebugEvent: findType("BuiltList"), + BuiltList_ExtensionEvent: findType("BuiltList"), BuiltList_dynamic: findType("BuiltList<@>"), BuiltList_nullable_Object: findType("BuiltList"), BuiltMap_dynamic_dynamic: findType("BuiltMap<@,@>"), diff --git a/dwds/lib/src/loaders/ddc_library_bundle.dart b/dwds/lib/src/loaders/ddc_library_bundle.dart index 220b73828..3fb592dde 100644 --- a/dwds/lib/src/loaders/ddc_library_bundle.dart +++ b/dwds/lib/src/loaders/ddc_library_bundle.dart @@ -223,10 +223,4 @@ window.\$dartLoader.loader.nextAttempt(); @override String? g3RelativePath(String absolutePath) => _g3RelativePath(absolutePath); - - @override - MetadataProvider createProvider(String entrypoint, AssetReader reader) => - // DDC library bundle format does not provide module names in the module - // metadata. - MetadataProvider(entrypoint, reader, useModuleName: false); } diff --git a/dwds/lib/src/loaders/strategy.dart b/dwds/lib/src/loaders/strategy.dart index f224f8e2d..e38cea083 100644 --- a/dwds/lib/src/loaders/strategy.dart +++ b/dwds/lib/src/loaders/strategy.dart @@ -171,7 +171,7 @@ abstract class LoadStrategy { /// Creates and returns a [MetadataProvider] with the given [entrypoint] and /// [reader]. MetadataProvider createProvider(String entrypoint, AssetReader reader) => - MetadataProvider(entrypoint, reader, useModuleName: true); + MetadataProvider(entrypoint, reader); /// Initializes a [MetadataProvider] for the application located at the /// provided [entrypoint]. @@ -182,6 +182,14 @@ abstract class LoadStrategy { // this method: return Future.value(); } + + Future reinitializeProviderAfterHotReload( + String entrypoint, + Map reloadedModulesToLibraries, + ) { + final provider = _providers[entrypoint]!; + return provider.reinitializeAfterHotReload(reloadedModulesToLibraries); + } } enum ReloadConfiguration { none, hotReload, hotRestart, liveReload } diff --git a/dwds/lib/src/services/chrome_proxy_service.dart b/dwds/lib/src/services/chrome_proxy_service.dart index 836a1de21..c98bbc8ab 100644 --- a/dwds/lib/src/services/chrome_proxy_service.dart +++ b/dwds/lib/src/services/chrome_proxy_service.dart @@ -17,6 +17,7 @@ import 'package:dwds/src/debugging/execution_context.dart'; import 'package:dwds/src/debugging/inspector.dart'; import 'package:dwds/src/debugging/instance.dart'; import 'package:dwds/src/debugging/location.dart'; +import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/debugging/modules.dart'; import 'package:dwds/src/debugging/remote_debugger.dart'; import 'package:dwds/src/debugging/skip_list.dart'; @@ -196,7 +197,7 @@ class ChromeProxyService implements VmServiceInterface { final modules = Modules(root); final locations = Locations(assetReader, modules, root); - final skipLists = SkipLists(); + final skipLists = SkipLists(root); final service = ChromeProxyService._( vm, root, @@ -234,25 +235,41 @@ class ChromeProxyService implements VmServiceInterface { } /// Reinitializes any caches so that they can be recomputed across hot reload. - // TODO(srujzs): We can maybe do better here than reinitializing all the data. - // Specifically, we can invalidate certain parts as we know what libraries - // will be stale, and therefore recompute information only for those libraries - // and possibly libraries that depend on them. Currently, there's no good - // separation between "existing" information and "new" information, making - // this difficult. - // https://github.com/dart-lang/webdev/issues/2628 - Future _reinitializeForHotReload() async { + /// + /// We use the [ModifiedModuleReport] to more efficiently invalidate caches. + Future _reinitializeForHotReload( + Map reloadedModules, + ) async { final entrypoint = inspector.appConnection.request.entrypointPath; - await globalToolConfiguration.loadStrategy.trackEntrypoint(entrypoint); - _initializeEntrypoint(entrypoint); - await inspector.initialize(); + final modifiedModuleReport = await globalToolConfiguration.loadStrategy + .reinitializeProviderAfterHotReload(entrypoint, reloadedModules); + await _initializeEntrypoint( + entrypoint, + modifiedModuleReport: modifiedModuleReport, + ); + await inspector.initialize(modifiedModuleReport: modifiedModuleReport); } /// Initializes metadata in [Locations], [Modules], and [ExpressionCompiler]. - void _initializeEntrypoint(String entrypoint) { - _locations.initialize(entrypoint); - _modules.initialize(entrypoint); - _skipLists.initialize(); + /// + /// If [modifiedModuleReport] is not null, only removes and reinitializes + /// modified metadata. + Future _initializeEntrypoint( + String entrypoint, { + ModifiedModuleReport? modifiedModuleReport, + }) async { + await _modules.initialize( + entrypoint, + modifiedModuleReport: modifiedModuleReport, + ); + await _locations.initialize( + entrypoint, + modifiedModuleReport: modifiedModuleReport, + ); + await _skipLists.initialize( + entrypoint, + modifiedModuleReport: modifiedModuleReport, + ); // We do not need to wait for compiler dependencies to be updated as the // [ExpressionEvaluator] is robust to evaluation requests during updates. safeUnawaited(_updateCompilerDependencies(entrypoint)); @@ -346,7 +363,7 @@ class ChromeProxyService implements VmServiceInterface { if (!newConnection) { await globalToolConfiguration.loadStrategy.trackEntrypoint(entrypoint); } - _initializeEntrypoint(entrypoint); + await _initializeEntrypoint(entrypoint); debugger.notifyPausedAtStart(); _inspector = await AppInspector.create( @@ -1206,11 +1223,13 @@ class ChromeProxyService implements VmServiceInterface { // Initiate a hot reload. _logger.info('Issuing \$dartHotReloadStartDwds request'); - await inspector.jsEvaluate( + final remoteObject = await inspector.jsEvaluate( '\$dartHotReloadStartDwds();', awaitPromise: true, returnByValue: true, ); + final reloadedModulesToLibraries = + (remoteObject.value as Map).cast(); if (!pauseIsolatesOnStart) { // Finish hot reload immediately. @@ -1245,7 +1264,7 @@ class ChromeProxyService implements VmServiceInterface { await pause(isolateId); await pausedEvent; - await _reinitializeForHotReload(); + await _reinitializeForHotReload(reloadedModulesToLibraries); // This lets the client know that we're ready for breakpoint management // and a resume. diff --git a/dwds/lib/src/version.dart b/dwds/lib/src/version.dart index 588ff7d61..2786a7b0a 100644 --- a/dwds/lib/src/version.dart +++ b/dwds/lib/src/version.dart @@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '24.4.0-wip'; +const packageVersion = '24.4.0'; diff --git a/dwds/pubspec.yaml b/dwds/pubspec.yaml index 7c0b832fc..f139974a6 100644 --- a/dwds/pubspec.yaml +++ b/dwds/pubspec.yaml @@ -1,6 +1,6 @@ name: dwds # Every time this changes you need to run `dart run build_runner build`. -version: 24.4.0-wip +version: 24.4.0 description: >- A service that proxies between the Chrome debug protocol and the Dart VM diff --git a/dwds/test/debugger_test.dart b/dwds/test/debugger_test.dart index bfe6cc15b..ec6fd766e 100644 --- a/dwds/test/debugger_test.dart +++ b/dwds/test/debugger_test.dart @@ -88,8 +88,8 @@ void main() async { FakeModules(), root, ); - locations.initialize('fake_entrypoint'); - skipLists = SkipLists(); + await locations.initialize('fake_entrypoint'); + skipLists = SkipLists(root); debugger = await Debugger.create( webkitDebugger, (_, __) {}, diff --git a/dwds/test/expression_evaluator_test.dart b/dwds/test/expression_evaluator_test.dart index a9a147224..7f32c276c 100644 --- a/dwds/test/expression_evaluator_test.dart +++ b/dwds/test/expression_evaluator_test.dart @@ -54,9 +54,9 @@ void main() async { final root = 'fakeRoot'; final entry = 'fake_entrypoint'; final locations = Locations(assetReader, modules, root); - locations.initialize(entry); + await locations.initialize(entry); - final skipLists = SkipLists(); + final skipLists = SkipLists(root); final debugger = await Debugger.create( webkitDebugger, (_, e) => debugEventController.sink.add(e), diff --git a/dwds/test/fixtures/fakes.dart b/dwds/test/fixtures/fakes.dart index 114c3e2a7..2b021101d 100644 --- a/dwds/test/fixtures/fakes.dart +++ b/dwds/test/fixtures/fakes.dart @@ -68,7 +68,7 @@ class FakeInspector implements AppInspector { } @override - Future initialize() async => {}; + Future initialize({ModifiedModuleReport? modifiedModuleReport}) async {} @override Future instanceRefFor(Object value) async => @@ -157,7 +157,10 @@ class FakeModules implements Modules { _path = path; @override - void initialize(String entrypoint) {} + Future initialize( + String entrypoint, { + ModifiedModuleReport? modifiedModuleReport, + }) async {} @override Future libraryForSource(String serverPath) async => Uri(path: _library); @@ -166,7 +169,10 @@ class FakeModules implements Modules { Future moduleForSource(String serverPath) async => _module; @override - Future> modules() async => {_module: _path}; + Future> sourcesForModule(String module) async => {_path}; + + @override + Future> modules() async => {_path: _module}; @override Future moduleForLibrary(String libraryUri) async => _module; @@ -406,12 +412,11 @@ class FakeStrategy extends LoadStrategy { } class FakeAssetReader implements AssetReader { - final String? _metadata; + String? metadata; final String? _dartSource; final String? _sourceMap; - const FakeAssetReader({metadata, dartSource, sourceMap}) - : _metadata = metadata, - _dartSource = dartSource, + FakeAssetReader({this.metadata, dartSource, sourceMap}) + : _dartSource = dartSource, _sourceMap = sourceMap; @override @@ -424,7 +429,7 @@ class FakeAssetReader implements AssetReader { @override Future metadataContents(String serverPath) { - return _throwUnimplementedOrReturnContents(_metadata); + return _throwUnimplementedOrReturnContents(metadata); } @override diff --git a/dwds/test/fixtures/utilities.dart b/dwds/test/fixtures/utilities.dart index 1fec81e30..6d779c1bc 100644 --- a/dwds/test/fixtures/utilities.dart +++ b/dwds/test/fixtures/utilities.dart @@ -196,9 +196,7 @@ class TestToolConfiguration extends ToolConfiguration { TestDebugSettings super.debugSettings = const TestDebugSettings.noDevTools(), TestBuildSettings buildSettings = const TestBuildSettings.dart(), - }) : super( - loadStrategy: TestStrategy(const FakeAssetReader(), buildSettings), - ); + }) : super(loadStrategy: TestStrategy(FakeAssetReader(), buildSettings)); TestToolConfiguration.withLoadStrategy({ TestAppMetadata super.appMetadata = const TestAppMetadata.externalApp(), diff --git a/dwds/test/location_test.dart b/dwds/test/location_test.dart index ba5e38a4b..e3a914320 100644 --- a/dwds/test/location_test.dart +++ b/dwds/test/location_test.dart @@ -20,7 +20,7 @@ final sourceMapContents = 'AAGV,IAFI,kCAAqC,QAAC;AACX,MAA/B,WAAM,AAAwB,0BAAP,QAAF,AAAE,KAAK,GAAP;' ';EAEzB","file":"main.ddc.js"}'; -void main() { +void main() async { const lines = 100; const lineLength = 150; final assetReader = FakeAssetReader(sourceMap: sourceMapContents); @@ -32,7 +32,7 @@ void main() { final modules = FakeModules(module: _module); final locations = Locations(assetReader, modules, ''); - locations.initialize('fake_entrypoint'); + await locations.initialize('fake_entrypoint'); group('JS locations |', () { const fakeRuntimeScriptId = '12'; diff --git a/dwds/test/metadata_test.dart b/dwds/test/metadata_test.dart index 54019467a..50d12c8e1 100644 --- a/dwds/test/metadata_test.dart +++ b/dwds/test/metadata_test.dart @@ -5,6 +5,8 @@ @Timeout(Duration(minutes: 2)) library; +import 'dart:convert'; + import 'package:dwds/src/debugging/metadata/module_metadata.dart'; import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:test/test.dart'; @@ -36,61 +38,50 @@ void main() { ); setGlobalsForTesting(toolConfiguration: toolConfiguration); test('can parse metadata with empty sources', () async { - for (final useModuleName in [true, false]) { - final provider = MetadataProvider( - 'foo.bootstrap.js', - FakeAssetReader(metadata: _emptySourceMetadata), - useModuleName: useModuleName, - ); - expect( - await provider.libraries, - contains('org-dartlang-app:///web/main.dart'), - ); - } + final provider = MetadataProvider( + 'foo.bootstrap.js', + FakeAssetReader(metadata: _emptySourceMetadata), + ); + expect( + await provider.libraries, + contains('org-dartlang-app:///web/main.dart'), + ); }); test('throws on metadata with absolute import uris', () async { - for (final useModuleName in [true, false]) { - final provider = MetadataProvider( - 'foo.bootstrap.js', - FakeAssetReader(metadata: _fileUriMetadata), - useModuleName: useModuleName, - ); - await expectLater( - provider.libraries, - throwsA(const TypeMatcher()), - ); - } + final provider = MetadataProvider( + 'foo.bootstrap.js', + FakeAssetReader(metadata: _fileUriMetadata), + ); + await expectLater( + provider.libraries, + throwsA(const TypeMatcher()), + ); }); test( 'module name exists if useModuleName and otherwise use module uri', () async { - for (final useModuleName in [true, false]) { - final provider = MetadataProvider( - 'foo.bootstrap.js', - FakeAssetReader(metadata: _emptySourceMetadata), - useModuleName: useModuleName, - ); - final modulePath = 'foo/web/main.ddc.js'; - final moduleName = 'web/main'; - final module = useModuleName ? moduleName : modulePath; - expect( - await provider.scriptToModule, - predicate>( - (scriptToModule) => - !scriptToModule.values.any( - (value) => value == (useModuleName ? modulePath : moduleName), - ), - ), - ); - expect(await provider.moduleToSourceMap, { - module: 'foo/web/main.ddc.js.map', - }); - expect(await provider.modulePathToModule, {modulePath: module}); - expect(await provider.moduleToModulePath, {module: modulePath}); - expect(await provider.modules, {module}); - } + final provider = MetadataProvider( + 'foo.bootstrap.js', + FakeAssetReader(metadata: _emptySourceMetadata), + ); + final modulePath = 'foo/web/main.ddc.js'; + final moduleName = 'web/main'; + final module = moduleName; + expect( + await provider.scriptToModule, + predicate>( + (scriptToModule) => + !scriptToModule.values.any((value) => value == modulePath), + ), + ); + expect(await provider.moduleToSourceMap, { + module: 'foo/web/main.ddc.js.map', + }); + expect(await provider.modulePathToModule, {modulePath: module}); + expect(await provider.moduleToModulePath, {module: modulePath}); + expect(await provider.modules, {module}); }, ); @@ -126,4 +117,160 @@ void main() { expect(parts[0], 'org-dartlang-app:///web/main.dart'); } }); + + String createMetadataContents( + Map> moduleToLibraries, + Map> libraryToParts, + ) { + final contents = StringBuffer(); + for (final MapEntry(key: module, value: libraries) + in moduleToLibraries.entries) { + final moduleMetadata = ModuleMetadata( + module, + 'load__web__$module', + 'foo/web/$module.ddc.js.map', + 'foo/web/$module.ddc.js', + ); + for (final library in libraries) { + moduleMetadata.addLibrary( + LibraryMetadata(library, library, libraryToParts[library] ?? []), + ); + } + contents.writeln(json.encode(moduleMetadata.toJson())); + } + contents.write('// intentionally empty: ...'); + return contents.toString(); + } + + Future validateProvider( + MetadataProvider provider, + Map> moduleToLibraries, + Map> libraryToParts, + ) async { + final expectedScriptToModule = {}; + final expectedModuleToSourceMap = {}; + final expectedModulePathToModule = {}; + final expectedModules = {}; + for (final MapEntry(key: module, value: libraries) + in moduleToLibraries.entries) { + for (final library in libraries) { + expectedScriptToModule[library] = module; + final parts = libraryToParts[library]; + if (parts != null) { + for (final part in parts) { + expectedScriptToModule[part] = module; + } + } + } + expectedModuleToSourceMap[module] = 'foo/web/$module.ddc.js.map'; + expectedModulePathToModule['foo/web/$module.ddc.js'] = module; + expectedModules.add(module); + } + + final scriptToModule = await provider.scriptToModule; + for (final MapEntry(key: script, value: module) + in expectedScriptToModule.entries) { + expect(scriptToModule[script], module); + } + + final moduleToSourceMap = await provider.moduleToSourceMap; + for (final MapEntry(key: module, value: sourceMap) + in expectedModuleToSourceMap.entries) { + expect(moduleToSourceMap[module], sourceMap); + } + + final modulePathToModule = await provider.modulePathToModule; + for (final MapEntry(key: modulePath, value: module) + in expectedModulePathToModule.entries) { + expect(modulePathToModule[modulePath], module); + } + + expect(await provider.modules, containsAll(expectedModules)); + } + + test('reinitialize produces correct ModifiedModuleReport', () async { + const moduleToLibraries = >{ + 'm1': [ + 'org-dartlang-app:///web/l1.dart', + 'org-dartlang-app:///web/l2.dart', + ], + 'm2': [ + 'org-dartlang-app:///web/l3.dart', + 'org-dartlang-app:///web/l4.dart', + ], + 'm3': [ + 'org-dartlang-app:///web/l5.dart', + 'org-dartlang-app:///web/l6.dart', + ], + }; + const libraryToParts = >{ + 'org-dartlang-app:///web/l1.dart': ['org-dartlang-app:///web/l1_p1.dart'], + 'org-dartlang-app:///web/l3.dart': ['org-dartlang-app:///web/l3_p1.dart'], + }; + final assetReader = FakeAssetReader( + metadata: createMetadataContents(moduleToLibraries, libraryToParts), + ); + final provider = MetadataProvider('foo.bootstrap.js', assetReader); + await validateProvider(provider, moduleToLibraries, libraryToParts); + + const newModuleToLibraries = >{ + 'm1': [ + 'org-dartlang-app:///web/l1.dart', + 'org-dartlang-app:///web/l2.dart', + ], + 'm3': ['org-dartlang-app:///web/l3.dart'], + 'm4': [ + 'org-dartlang-app:///web/l4.dart', + 'org-dartlang-app:///web/l7.dart', + ], + }; + const newLibraryToParts = >{ + 'org-dartlang-app:///web/l2.dart': ['org-dartlang-app:///web/l1_p1.dart'], + 'org-dartlang-app:///web/l3.dart': ['org-dartlang-app:///web/l3_p2.dart'], + 'org-dartlang-app:///web/l7.dart': ['org-dartlang-app:///web/l7_p1.dart'], + }; + const reloadedModulesToLibraries = >{ + 'm3': ['org-dartlang-app:///web/l3.dart'], + 'm4': [ + 'org-dartlang-app:///web/l4.dart', + 'org-dartlang-app:///web/l7.dart', + ], + }; + assetReader.metadata = createMetadataContents( + newModuleToLibraries, + newLibraryToParts, + ); + final modifiedModuleReport = await provider.reinitializeAfterHotReload( + reloadedModulesToLibraries, + ); + expect(modifiedModuleReport.deletedModules, ['m2']); + expect( + modifiedModuleReport.deletedLibraries, + unorderedEquals([ + 'org-dartlang-app:///web/l5.dart', + 'org-dartlang-app:///web/l6.dart', + ]), + ); + expect(modifiedModuleReport.reloadedModules, ['m3', 'm4']); + expect( + modifiedModuleReport.reloadedLibraries, + unorderedEquals([ + 'org-dartlang-app:///web/l3.dart', + 'org-dartlang-app:///web/l4.dart', + 'org-dartlang-app:///web/l7.dart', + ]), + ); + expect(modifiedModuleReport.modifiedModules, ['m2', 'm3', 'm4']); + expect( + modifiedModuleReport.modifiedLibraries, + unorderedEquals([ + 'org-dartlang-app:///web/l3.dart', + 'org-dartlang-app:///web/l4.dart', + 'org-dartlang-app:///web/l5.dart', + 'org-dartlang-app:///web/l6.dart', + 'org-dartlang-app:///web/l7.dart', + ]), + ); + await validateProvider(provider, newModuleToLibraries, newLibraryToParts); + }); } diff --git a/dwds/test/skip_list_test.dart b/dwds/test/skip_list_test.dart index d1c626529..0707fdc0b 100644 --- a/dwds/test/skip_list_test.dart +++ b/dwds/test/skip_list_test.dart @@ -20,11 +20,11 @@ void main() { const fakeRuntimeScriptId = '12'; group('SkipLists', () { setUp(() { - skipLists = SkipLists(); + skipLists = SkipLists(''); }); test('do not include known ranges', () { - final skipList = skipLists.compute('123', { + final skipList = skipLists.compute('123', '456', { Location.from( 'foo', TargetLineEntry(1, []), @@ -47,7 +47,7 @@ void main() { }); test('do not include start of the file', () { - final skipList = skipLists.compute('123', { + final skipList = skipLists.compute('123', '456', { Location.from( 'foo', TargetLineEntry(0, []), @@ -69,7 +69,7 @@ void main() { }); test('does not depend on order of locations', () { - final skipList = skipLists.compute('123', { + final skipList = skipLists.compute('123', '456', { Location.from( 'foo', TargetLineEntry(10, []), @@ -92,14 +92,14 @@ void main() { test('contains the provided id', () { final id = '123'; - final skipList = skipLists.compute(id, {}); + final skipList = skipLists.compute(id, '456', {}); for (final range in skipList) { expect(range['scriptId'], id); } }); test('ignores the whole file if provided no locations', () { - final skipList = skipLists.compute('123', {}); + final skipList = skipLists.compute('123', '456', {}); expect(skipList.length, 1); _validateRange(skipList.first, 0, 0, maxValue, maxValue); }); diff --git a/dwds/web/reloader/ddc_library_bundle_restarter.dart b/dwds/web/reloader/ddc_library_bundle_restarter.dart index b9bffc843..6ce3faea2 100644 --- a/dwds/web/reloader/ddc_library_bundle_restarter.dart +++ b/dwds/web/reloader/ddc_library_bundle_restarter.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'package:dwds/src/utilities/shared.dart'; @@ -85,7 +86,7 @@ class DdcLibraryBundleRestarter implements Restarter { } @override - Future> hotReloadStart(String hotReloadSourcesPath) async { + Future hotReloadStart(String hotReloadSourcesPath) async { final completer = Completer(); final xhr = _XMLHttpRequest(); xhr.withCredentials = true; @@ -101,13 +102,17 @@ class DdcLibraryBundleRestarter implements Restarter { xhr.send(); final responseText = await completer.future; - final srcLibraries = (json.decode(responseText) as List).cast(); + final srcModuleLibraries = (json.decode(responseText) as List).cast(); final filesToLoad = JSArray(); + final moduleMap = JSObject(); final librariesToReload = JSArray(); - for (final srcLibrary in srcLibraries) { - final srcLibraryCast = srcLibrary.cast(); - filesToLoad.push((srcLibraryCast['src'] as String).toJS); - final libraries = (srcLibraryCast['libraries'] as List).cast(); + for (final srcModuleLibrary in srcModuleLibraries) { + final srcModuleLibraryCast = srcModuleLibrary.cast(); + filesToLoad.push((srcModuleLibraryCast['src'] as String).toJS); + final module = srcModuleLibraryCast['module'] as String; + final libraries = + (srcModuleLibraryCast['libraries'] as List).cast(); + moduleMap[module] = libraries.jsify(); for (final library in libraries) { librariesToReload.push(library.toJS); } @@ -117,7 +122,7 @@ class DdcLibraryBundleRestarter implements Restarter { _capturedHotReloadEndCallback = hotReloadEndCallback; }.toJS; await _dartDevEmbedder.hotReload(filesToLoad, librariesToReload).toDart; - return librariesToReload; + return moduleMap; } @override diff --git a/dwds/web/reloader/ddc_restarter.dart b/dwds/web/reloader/ddc_restarter.dart index 095f21ef1..30399cfcb 100644 --- a/dwds/web/reloader/ddc_restarter.dart +++ b/dwds/web/reloader/ddc_restarter.dart @@ -47,7 +47,7 @@ class DdcRestarter implements Restarter { ); @override - Future> hotReloadStart(String hotReloadSourcesPath) => + Future hotReloadStart(String hotReloadSourcesPath) => throw UnimplementedError( 'Hot reload is not supported for the DDC module format.', ); diff --git a/dwds/web/reloader/manager.dart b/dwds/web/reloader/manager.dart index 22c4b0fb4..dc6c5a0b4 100644 --- a/dwds/web/reloader/manager.dart +++ b/dwds/web/reloader/manager.dart @@ -43,13 +43,15 @@ class ReloadingManager { } /// Computes the sources and libraries to reload, loads them into the page, - /// and returns the list of libraries using [hotReloadSourcesPath] as the path - /// to a JSONified list of maps which follows the following format: + /// and returns a map of module names to libraries using + /// [hotReloadSourcesPath] as the path to a JSONified list of maps which + /// follows the following format: /// /// ```json /// [ /// { /// "src": "", + /// "module": "", /// "libraries": ["", ""], /// }, /// ] @@ -57,9 +59,10 @@ class ReloadingManager { /// /// `src`: A string that corresponds to the file path containing a DDC library /// bundle. + /// `module`: The name of the library bundle in `src`. /// `libraries`: An array of strings containing the libraries that were /// compiled in `src`. - Future> hotReloadStart(String hotReloadSourcesPath) => + Future hotReloadStart(String hotReloadSourcesPath) => _restarter.hotReloadStart(hotReloadSourcesPath); /// Does a hard reload of the application. diff --git a/dwds/web/reloader/require_restarter.dart b/dwds/web/reloader/require_restarter.dart index 0e650629a..01115fffb 100644 --- a/dwds/web/reloader/require_restarter.dart +++ b/dwds/web/reloader/require_restarter.dart @@ -168,7 +168,7 @@ class RequireRestarter implements Restarter { ); @override - Future> hotReloadStart(String hotReloadSourcesPath) => + Future hotReloadStart(String hotReloadSourcesPath) => throw UnimplementedError( 'Hot reload is not supported for the AMD module format.', ); diff --git a/dwds/web/reloader/restarter.dart b/dwds/web/reloader/restarter.dart index e3ca0f70f..31cb09484 100644 --- a/dwds/web/reloader/restarter.dart +++ b/dwds/web/reloader/restarter.dart @@ -14,13 +14,15 @@ abstract class Restarter { Future hotReloadEnd(); /// Computes the sources and libraries to reload, loads them into the page, - /// and returns the list of libraries using [hotReloadSourcesPath] as the path - /// to a JSONified list of maps which follows the following format: + /// and returns a map of module names to libraries using + /// [hotReloadSourcesPath] as the path to a JSONified list of maps which + /// follows the following format: /// /// ```json /// [ /// { /// "src": "", + /// "module": "", /// "libraries": ["", ""], /// }, /// ] @@ -28,7 +30,8 @@ abstract class Restarter { /// /// `src`: A string that corresponds to the file path containing a DDC library /// bundle. + /// `module`: The name of the library bundle in `src`. /// `libraries`: An array of strings containing the libraries that were /// compiled in `src`. - Future> hotReloadStart(String hotReloadSourcesPath); + Future hotReloadStart(String hotReloadSourcesPath); } diff --git a/frontend_server_common/lib/src/devfs.dart b/frontend_server_common/lib/src/devfs.dart index 02a24839e..ace83aa7c 100644 --- a/frontend_server_common/lib/src/devfs.dart +++ b/frontend_server_common/lib/src/devfs.dart @@ -253,10 +253,11 @@ class WebDevFS { static const String reloadScriptsFileName = 'reload_scripts.json'; /// Given a list of [modules] that need to be reloaded, writes a file that - /// contains a list of objects each with two fields: + /// contains a list of objects each with three fields: /// /// `src`: A string that corresponds to the file path containing a DDC library /// bundle. + /// `module`: The name of the library bundle in `src`. /// `libraries`: An array of strings containing the libraries that were /// compiled in `src`. /// @@ -265,6 +266,7 @@ class WebDevFS { /// [ /// { /// "src": "", + /// "module": "", /// "libraries": ["", ""], /// }, /// ] @@ -286,6 +288,7 @@ class WebDevFS { final libraries = metadata.libraries.keys.toList(); moduleToLibrary.add({ 'src': _findModuleToLoad(module, entrypointDirectory), + 'module': metadata.name, 'libraries': libraries }); }