Skip to content

Commit 84e197e

Browse files
author
Anna Gringauze
authored
Migrate debugging class and library helpers to null safety (#1669)
* Migrate modules and locations to null safety * Remove circular dependencies * Remove circular dependencies and unused code * Simplified expression evaluation code * Cleanup * Fix failing tests * Move more helpers from Domain to ChromeProxyService * Build * Migrate some files in debugger directory to null safety * Minor fixes * Migrate debugging class and library helpers to null safety * Update AppInspectorInterface * Fix crash in inferring library root
1 parent 2684428 commit 84e197e

File tree

6 files changed

+86
-67
lines changed

6 files changed

+86
-67
lines changed

dwds/lib/src/debugging/classes.dart

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.import 'dart:async';
44

5-
// @dart = 2.9
6-
75
import 'package:vm_service/vm_service.dart';
86
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
97

@@ -26,7 +24,9 @@ class ClassHelper extends Domain {
2624
classRefForUnknown
2725
];
2826
for (var classRef in staticClasses) {
29-
_classes[classRef.id] = Class(
27+
final classId = classRef.id;
28+
if (classId != null) {
29+
_classes[classId] = Class(
3030
name: classRef.name,
3131
isAbstract: false,
3232
isConst: false,
@@ -35,15 +35,17 @@ class ClassHelper extends Domain {
3535
fields: [],
3636
functions: [],
3737
subclasses: [],
38-
id: classRef.id,
39-
traceAllocations: false);
38+
id: classId,
39+
traceAllocations: false,
40+
);
41+
}
4042
}
4143
}
4244

4345
/// Returns the [Class] that corresponds to the provided [objectId].
4446
///
4547
/// If a corresponding class does not exist it will return null.
46-
Future<Class> forObjectId(String objectId) async {
48+
Future<Class?> forObjectId(String objectId) async {
4749
if (!objectId.startsWith('classes|')) return null;
4850
var clazz = _classes[objectId];
4951
if (clazz != null) return clazz;
@@ -66,12 +68,18 @@ class ClassHelper extends Domain {
6668

6769
/// Constructs a [Class] instance for the provided [LibraryRef] and
6870
/// [ClassRef].
69-
Future<Class> _constructClass(
71+
Future<Class?> _constructClass(
7072
LibraryRef libraryRef, ClassRef classRef) async {
71-
final rawName = classRef.name.split('<').first;
73+
final libraryUri = libraryRef.uri;
74+
final className = classRef.name;
75+
final classId = classRef.id;
76+
77+
if (libraryUri == null || classId == null || className == null) return null;
78+
79+
final rawName = className.split('<').first;
7280
final expression = '''
7381
(function() {
74-
${globalLoadStrategy.loadLibrarySnippet(libraryRef.uri)}
82+
${globalLoadStrategy.loadLibrarySnippet(libraryUri)}
7583
var result = {};
7684
var clazz = library["$rawName"];
7785
var descriptor = {
@@ -171,7 +179,7 @@ class ClassHelper extends Domain {
171179
classDescriptor['staticMethods'] as Map<String, dynamic>;
172180
methodDescriptors.addAll(staticMethodDescriptors);
173181
methodDescriptors.forEach((name, descriptor) {
174-
final methodId = 'methods|${classRef.id}|$name';
182+
final methodId = 'methods|$classId|$name';
175183
methodRefs.add(FuncRef(
176184
id: methodId,
177185
name: name,
@@ -237,7 +245,7 @@ class ClassHelper extends Domain {
237245
fields: fieldRefs,
238246
functions: methodRefs,
239247
subclasses: [],
240-
id: classRef.id,
248+
id: classId,
241249
traceAllocations: false);
242250
}
243251
}

dwds/lib/src/debugging/inspector.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,12 @@ class AppInspector implements AppInspectorInterface {
301301
/// Evaluate [expression] by calling Chrome's Runtime.evaluate.
302302
@override
303303
Future<RemoteObject> jsEvaluate(String expression,
304-
{bool awaitPromise = false}) async {
304+
{bool returnByValue = false, bool awaitPromise = false}) async {
305305
// TODO(alanknight): Support a version with arguments if needed.
306306
WipResponse result;
307307
result = await remoteDebugger.sendCommand('Runtime.evaluate', params: {
308308
'expression': expression,
309+
'returnByValue': returnByValue,
309310
'awaitPromise': awaitPromise,
310311
'contextId': await contextId,
311312
});

dwds/lib/src/debugging/libraries.dart

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.import 'dart:async';
44

5-
// @dart = 2.9
6-
5+
import 'package:collection/collection.dart';
76
import 'package:logging/logging.dart';
87
import 'package:vm_service/vm_service.dart';
8+
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
99

1010
import '../loaders/strategy.dart';
1111
import '../utilities/domain.dart';
12+
import '../services/chrome_debug_exception.dart';
1213
import 'metadata/class.dart';
1314

1415
/// Keeps track of Dart libraries available in the running application.
@@ -21,24 +22,24 @@ class LibraryHelper extends Domain {
2122
/// Map of libraryRef ID to [LibraryRef].
2223
final _libraryRefsById = <String, LibraryRef>{};
2324

24-
LibraryRef _rootLib;
25+
LibraryRef? _rootLib;
2526

2627
LibraryHelper(AppInspectorInterface appInspector) {
2728
inspector = appInspector;
2829
}
2930

3031
Future<LibraryRef> get rootLib async {
31-
if (_rootLib != null) return _rootLib;
32+
if (_rootLib != null) return _rootLib!;
3233
// TODO: read entrypoint from app metadata.
3334
// Issue: https://github.com/dart-lang/webdev/issues/1290
3435
final libraries = await libraryRefs;
35-
_rootLib = libraries.firstWhere((lib) => lib.name.contains('org-dartlang'),
36-
orElse: () => null);
36+
_rootLib = libraries
37+
.firstWhereOrNull((lib) => lib.name?.contains('org-dartlang') ?? false);
3738
_rootLib = _rootLib ??
38-
libraries.firstWhere((lib) => lib.name.contains('main'),
39-
orElse: () => null);
39+
libraries
40+
.firstWhereOrNull((lib) => lib.name?.contains('main') ?? false);
4041
_rootLib = _rootLib ?? (libraries.isNotEmpty ? libraries.last : null);
41-
return _rootLib;
42+
return _rootLib!;
4243
}
4344

4445
/// Returns all libraryRefs in the app.
@@ -56,22 +57,29 @@ class LibraryHelper extends Domain {
5657
return _libraryRefsById.values.toList();
5758
}
5859

59-
Future<Library> libraryFor(LibraryRef libraryRef) async {
60-
final library = _librariesById[libraryRef.id];
61-
if (library != null) return library;
62-
return _librariesById[libraryRef.id] = await _constructLibrary(libraryRef);
60+
Future<Library?> libraryFor(LibraryRef libraryRef) async {
61+
final libraryId = libraryRef.id;
62+
if (libraryId == null) return null;
63+
final library =
64+
_librariesById[libraryId] ?? await _constructLibrary(libraryRef);
65+
if (library == null) return null;
66+
return _librariesById[libraryId] = library;
6367
}
6468

65-
Future<LibraryRef> libraryRefFor(String objectId) async {
69+
Future<LibraryRef?> libraryRefFor(String objectId) async {
6670
if (_libraryRefsById.isEmpty) await libraryRefs;
6771
return _libraryRefsById[objectId];
6872
}
6973

70-
Future<Library> _constructLibrary(LibraryRef libraryRef) async {
74+
Future<Library?> _constructLibrary(LibraryRef libraryRef) async {
75+
final libraryId = libraryRef.id;
76+
final libraryUri = libraryRef.uri;
77+
if (libraryId == null || libraryUri == null) return null;
78+
7179
// Fetch information about all the classes in this library.
7280
final expression = '''
7381
(function() {
74-
${globalLoadStrategy.loadLibrarySnippet(libraryRef.uri)}
82+
${globalLoadStrategy.loadLibrarySnippet(libraryUri)}
7583
var result = {};
7684
var classes = Object.values(Object.getOwnPropertyDescriptors(library))
7785
.filter((p) => 'value' in p)
@@ -88,41 +96,41 @@ class LibraryHelper extends Domain {
8896
return result;
8997
})()
9098
''';
91-
final result =
92-
await inspector.remoteDebugger.sendCommand('Runtime.evaluate', params: {
93-
'expression': expression,
94-
'returnByValue': true,
95-
'contextId': await inspector.contextId,
96-
});
97-
List<ClassRef> classRefs;
98-
if (result.result.containsKey('exceptionDetails')) {
99+
RemoteObject? result;
100+
try {
101+
result = await inspector.jsEvaluate(expression, returnByValue: true);
102+
} on ChromeDebugException catch (_) {
99103
// Unreferenced libraries are not loaded at runtime,
100104
// return empty library object for consistency among
101105
// VM Service implementations.
102106
// TODO: Collect library and class information from debug symbols.
103107
_logger.warning('Library ${libraryRef.uri} is not loaded. '
104108
'This can happen for unreferenced libraries.');
105-
} else {
109+
}
110+
List<ClassRef>? classRefs;
111+
if (result != null) {
106112
final classDescriptors =
107-
(result.result['result']['value']['classes'] as List)
108-
.cast<Map<String, Object>>();
109-
classRefs = classDescriptors.map<ClassRef>((classDescriptor) {
113+
((result.value as Map<String, dynamic>)['classes'] as List?)
114+
?.cast<Map<String, Object>>();
115+
classRefs = classDescriptors?.map<ClassRef>((classDescriptor) {
110116
final classMetaData = ClassMetaData(
111-
jsName: classDescriptor['name'],
112-
libraryId: libraryRef.id,
113-
dartName: classDescriptor['dartName']);
117+
jsName: classDescriptor['name'],
118+
libraryId: libraryRef.id,
119+
dartName: classDescriptor['dartName'],
120+
);
114121
return classMetaData.classRef;
115122
}).toList();
116123
}
117124
return Library(
118-
name: libraryRef.name,
119-
uri: libraryRef.uri,
120-
debuggable: true,
121-
dependencies: [],
122-
scripts: await inspector.scriptRefsForLibrary(libraryRef.id),
123-
variables: [],
124-
functions: [],
125-
classes: classRefs,
126-
id: libraryRef.id);
125+
name: libraryRef.name,
126+
uri: libraryRef.uri,
127+
debuggable: true,
128+
dependencies: [],
129+
scripts: await inspector.scriptRefsForLibrary(libraryId),
130+
variables: [],
131+
functions: [],
132+
classes: classRefs,
133+
id: libraryId,
134+
);
127135
}
128136
}

dwds/lib/src/debugging/metadata/class.dart

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.import 'dart:async';
44

5-
// @dart = 2.9
6-
5+
import 'package:dwds/src/utilities/domain.dart';
76
import 'package:vm_service/vm_service.dart';
87
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
98

10-
import '../../debugging/inspector.dart';
119
import '../../debugging/remote_debugger.dart';
1210
import '../../loaders/strategy.dart';
1311
import '../../services/chrome_debug_exception.dart';
@@ -22,34 +20,36 @@ final classRefForString = classRefFor('dart:core', InstanceKind.kString);
2220
final classRefForUnknown = classRefFor('dart:core', 'Unknown');
2321

2422
/// Returns a [ClassRef] for the provided library ID and class name.
25-
ClassRef classRefFor(String libraryId, String name) => ClassRef(
23+
ClassRef classRefFor(String? libraryId, String? name) => ClassRef(
2624
id: 'classes|$libraryId|$name',
2725
name: name,
28-
library: LibraryRef(id: libraryId, name: libraryId, uri: libraryId));
26+
library: libraryId == null
27+
? null
28+
: LibraryRef(id: libraryId, name: libraryId, uri: libraryId));
2929

3030
/// Meta data for a remote Dart class in Chrome.
3131
class ClassMetaData {
3232
/// The name of the JS constructor for the object.
3333
///
3434
/// This may be a constructor for a Dart, but it's still a JS name. For
3535
/// example, 'Number', 'JSArray', 'Object'.
36-
final String jsName;
36+
final String? jsName;
3737

3838
/// The length of the object, if applicable.
39-
final int length;
39+
final int? length;
4040

4141
/// The dart type name for the object.
4242
///
4343
/// For example, 'int', 'List<String>', 'Null'
44-
final String dartName;
44+
final String? dartName;
4545

4646
/// The library identifier, which is the URI of the library.
47-
final String libraryId;
47+
final String? libraryId;
4848

4949
factory ClassMetaData(
50-
{Object jsName, Object libraryId, Object dartName, Object length}) {
51-
return ClassMetaData._(jsName as String, libraryId as String,
52-
dartName as String, int.tryParse('$length'));
50+
{Object? jsName, Object? libraryId, Object? dartName, Object? length}) {
51+
return ClassMetaData._(jsName as String?, libraryId as String?,
52+
dartName as String?, int.tryParse('$length'));
5353
}
5454

5555
ClassMetaData._(this.jsName, this.libraryId, this.dartName, this.length);
@@ -62,8 +62,8 @@ class ClassMetaData {
6262
/// Returns the [ClassMetaData] for the Chrome [remoteObject].
6363
///
6464
/// Returns null if the [remoteObject] is not a Dart class.
65-
static Future<ClassMetaData> metaDataFor(RemoteDebugger remoteDebugger,
66-
RemoteObject remoteObject, AppInspector inspector) async {
65+
static Future<ClassMetaData?> metaDataFor(RemoteDebugger remoteDebugger,
66+
RemoteObject remoteObject, AppInspectorInterface inspector) async {
6767
try {
6868
final evalExpression = '''
6969
function(arg) {

dwds/lib/src/utilities/domain.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ abstract class AppInspectorInterface {
7272
String targetId, String selector, List<dynamic> arguments);
7373

7474
/// Evaluate [expression] by calling Chrome's `Runtime.evaluate`.
75-
Future<RemoteObject> jsEvaluate(String expression);
75+
Future<RemoteObject> jsEvaluate(String expression,
76+
{bool returnByValue = false, bool awaitPromise = false});
7677

7778
/// Lookup an `object` from some isolate by its [objectId].
7879
Future<Obj> getObject(String objectId, {int offset, int count});

dwds/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies:
1313
async: ^2.9.0
1414
built_collection: ^5.1.1
1515
built_value: ^8.3.0
16+
collection: ^1.15.0
1617
crypto: ^3.0.2
1718
dds: ^2.2.5
1819
file: ^6.1.2

0 commit comments

Comments
 (0)