Skip to content

Migrate modules and locations to null safety #1663

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 32 additions & 18 deletions dwds/lib/src/debugging/location.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
// 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.

// @dart = 2.9

import 'package:async/async.dart';
import 'package:dwds/src/loaders/require.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:source_maps/parser.dart';
import 'package:source_maps/source_maps.dart';
Expand Down Expand Up @@ -46,7 +45,7 @@ class Location {
// https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
return Location._(
JsLocation.fromZeroBased(module, jsLine, jsColumn),
DartLocation.fromZeroBased(dartUri, dartLine, dartColumn),
DartLocation.fromZeroBased(dartUri, dartLine ?? 0, dartColumn ?? 0),
);
}

Expand Down Expand Up @@ -118,6 +117,8 @@ class JsLocation {

/// Contains meta data for known [Location]s.
class Locations {
final _logger = Logger('Locations');

/// [Location] data for Dart server path.
final Map<String, Set<Location>> _sourceToLocation = {};
final Map<String, AsyncMemoizer<Set<Location>>> _locationMemoizer = {};
Expand All @@ -134,7 +135,7 @@ class Locations {
final Modules _modules;
final String _root;

String _entrypoint;
late String _entrypoint;

Locations(this._assetReader, this._modules, this._root);

Expand All @@ -151,7 +152,11 @@ class Locations {
/// Returns all [Location] data for a provided Dart source.
Future<Set<Location>> locationsForDart(String serverPath) async {
final module = await _modules.moduleForSource(serverPath);
await _locationsForModule(module);
if (module == null) {
_logger.warning('No module for server path $serverPath');
} else {
await _locationsForModule(module);
}
return _sourceToLocation[serverPath] ?? {};
}

Expand All @@ -161,21 +166,26 @@ class Locations {
_entrypoint, Uri.parse(url).path);
final cache = _moduleToLocations[module];
if (cache != null) return cache;
return await _locationsForModule(module) ?? {};
if (module == null) {
_logger.warning('No module for $url');
} else {
await _locationsForModule(module);
}
return _moduleToLocations[module] ?? {};
}

/// Find the [Location] for the given Dart source position.
///
/// The [line] number is 1-based.
Future<Location> locationForDart(DartUri uri, int line, int column) async {
Future<Location?> locationForDart(DartUri uri, int line, int column) async {
final locations = await locationsForDart(uri.serverPath);
return _bestDartLocation(locations, line, column);
}

/// Find the [Location] for the given JS source position.
///
/// The [line] number is 0-based.
Future<Location> locationForJs(String url, int line, int column) async {
Future<Location?> locationForJs(String url, int line, int column) async {
final locations = await locationsForUrl(url);
return _bestJsLocation(locations, line, column);
}
Expand All @@ -185,9 +195,9 @@ class Locations {
/// Dart columns for breakpoints are either exact or start at the
/// beginning of the line - return the first existing location
/// that comes after the given column.
Location _bestDartLocation(
Location? _bestDartLocation(
Iterable<Location> locations, int line, int column) {
Location bestLocation;
Location? bestLocation;
for (var location in locations) {
if (location.dartLocation.line == line &&
location.dartLocation.column >= column) {
Expand All @@ -209,8 +219,9 @@ class Locations {
/// the closest location to the current one:
///
/// https://github.com/microsoft/vscode-js-debug/blob/536f96bae61a3d87546b61bc7916097904c81429/src/common/sourceUtils.ts#L286
Location _bestJsLocation(Iterable<Location> locations, int line, int column) {
Location bestLocation;
Location? _bestJsLocation(
Iterable<Location> locations, int line, int column) {
Location? bestLocation;
for (var location in locations) {
if (location.jsLocation.compareToLine(line, column) <= 0) {
bestLocation ??= location;
Expand Down Expand Up @@ -239,9 +250,10 @@ class Locations {
.add(location);
}
for (var lineNumber in lineNumberToLocation.keys) {
final locations = lineNumberToLocation[lineNumber]!;
tokenPosTable.add([
lineNumber,
for (var location in lineNumberToLocation[lineNumber]) ...[
for (var location in locations) ...[
location.tokenPos,
location.dartLocation.column
]
Expand All @@ -257,13 +269,15 @@ class Locations {
///
/// This will populate the [_sourceToLocation] and [_moduleToLocations] maps.
Future<Set<Location>> _locationsForModule(String module) async {
_locationMemoizer.putIfAbsent(module, () => AsyncMemoizer());
final memoizer =
_locationMemoizer.putIfAbsent(module, () => AsyncMemoizer());

return await _locationMemoizer[module].runOnce(() async {
if (module == null) return {};
if (_moduleToLocations[module] != null) return _moduleToLocations[module];
return await memoizer.runOnce(() async {
if (_moduleToLocations.containsKey(module)) {
return _moduleToLocations[module]!;
}
final result = <Location>{};
if (module?.isEmpty ?? true) return _moduleToLocations[module] = result;
if (module.isEmpty) return _moduleToLocations[module] = result;
if (module.endsWith('dart_sdk') || module.endsWith('dart_library')) {
return result;
}
Expand Down
42 changes: 24 additions & 18 deletions dwds/lib/src/debugging/modules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// 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.

// @dart = 2.9

import 'package:async/async.dart';
import 'package:logging/logging.dart';

import '../loaders/strategy.dart';
import '../utilities/dart_uri.dart';

/// Tracks modules for the compiled application.
class Modules {
final _logger = Logger('Modules');
final String _root;

// The Dart server path to containing module.
Expand All @@ -22,9 +22,9 @@ class Modules {

final Map<String, String> _libraryToModule = {};

String _entrypoint;
late String _entrypoint;

Modules(String root) : _root = root == '' ? '/' : root;
Modules(this._root);

/// Initializes the mapping from source to module.
///
Expand All @@ -41,18 +41,18 @@ class Modules {
}

/// Returns the containing module for the provided Dart server path.
Future<String> moduleForSource(String serverPath) async {
Future<String?> moduleForSource(String serverPath) async {
await _moduleMemoizer.runOnce(_initializeMapping);
return _sourceToModule[serverPath];
}

/// Returns the containing library importUri for the provided Dart server path.
Future<Uri> libraryForSource(String serverPath) async {
Future<Uri?> libraryForSource(String serverPath) async {
await _moduleMemoizer.runOnce(_initializeMapping);
return _sourceToLibrary[serverPath];
}

Future<String> moduleForlibrary(String libraryUri) async {
Future<String?> moduleForlibrary(String libraryUri) async {
await _moduleMemoizer.runOnce(_initializeMapping);
return _libraryToModule[libraryUri];
}
Expand All @@ -71,21 +71,27 @@ class Modules {
final scriptToModule = await provider.scriptToModule;

for (var library in libraryToScripts.keys) {
final scripts = libraryToScripts[library]!;
final libraryServerPath = library.startsWith('dart:')
? library
: DartUri(library, _root).serverPath;

_sourceToModule[libraryServerPath] = scriptToModule[library];
_sourceToLibrary[libraryServerPath] = Uri.parse(library);
_libraryToModule[library] = scriptToModule[library];

for (var script in libraryToScripts[library]) {
final scriptServerPath = script.startsWith('dart:')
? script
: DartUri(script, _root).serverPath;

_sourceToModule[scriptServerPath] = scriptToModule[library];
_sourceToLibrary[scriptServerPath] = Uri.parse(library);
if (scriptToModule.containsKey(library)) {
final module = scriptToModule[library]!;
_sourceToModule[libraryServerPath] = module;
_sourceToLibrary[libraryServerPath] = Uri.parse(library);
_libraryToModule[library] = module;

for (var script in scripts) {
final scriptServerPath = script.startsWith('dart:')
? script
: DartUri(script, _root).serverPath;

_sourceToModule[scriptServerPath] = module;
_sourceToLibrary[scriptServerPath] = Uri.parse(library);
}
} else {
_logger.warning('No module found for library $library');
}
}
}
Expand Down
1 change: 1 addition & 0 deletions dwds/test/debugger_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ void main() async {
globalLoadStrategy = TestStrategy();
final root = 'fakeRoot';
locations = Locations(FakeAssetReader(), FakeModules(), root);
locations.initialize('fake_entrypoint');
skipLists = SkipLists();
debugger = await Debugger.create(
webkitDebugger,
Expand Down
1 change: 1 addition & 0 deletions dwds/test/location_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void main() {
final assetReader = FakeAssetReader();
final modules = MockModules();
final locations = Locations(assetReader, modules, '');
locations.initialize('fake_entrypoint');

group('JS locations |', () {
group('location |', () {
Expand Down