Skip to content

Commit 95ce058

Browse files
author
Anna Gringauze
authored
Migrate debugger instance helper to null safety (#1677)
* 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 * Migrate debugger instance helper to null safety
1 parent 558abc1 commit 95ce058

File tree

1 file changed

+87
-54
lines changed

1 file changed

+87
-54
lines changed

dwds/lib/src/debugging/instance.dart

Lines changed: 87 additions & 54 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.
44

5-
// @dart = 2.9
6-
75
import 'dart:math';
86

97
import 'package:vm_service/vm_service.dart';
@@ -30,7 +28,7 @@ class InstanceHelper extends Domain {
3028

3129
/// Creates an [InstanceRef] for a primitive [RemoteObject].
3230
static InstanceRef _primitiveInstanceRef(
33-
String kind, RemoteObject remoteObject) {
31+
String kind, RemoteObject? remoteObject) {
3432
final classRef = classRefFor('dart:core', kind);
3533
return InstanceRef(
3634
identityHashCode: dartIdFor(remoteObject?.value).hashCode,
@@ -41,21 +39,24 @@ class InstanceHelper extends Domain {
4139
}
4240

4341
/// Creates an [Instance] for a primitive [RemoteObject].
44-
Instance _primitiveInstance(String kind, RemoteObject remote) {
45-
if (remote?.objectId == null) return null;
42+
Instance? _primitiveInstance(String kind, RemoteObject? remote) {
43+
final objectId = remote?.objectId;
44+
if (objectId == null) return null;
4645
return Instance(
47-
identityHashCode: remote.objectId.hashCode,
48-
id: remote.objectId,
46+
identityHashCode: objectId.hashCode,
47+
id: objectId,
4948
kind: kind,
5049
classRef: classRefFor('dart:core', kind))
51-
..valueAsString = '${remote.value}';
50+
..valueAsString = '${remote?.value}';
5251
}
5352

54-
Instance _stringInstanceFor(
55-
RemoteObject remoteObject, int offset, int count) {
53+
Instance? _stringInstanceFor(
54+
RemoteObject? remoteObject, int? offset, int? count) {
5655
// TODO(#777) Consider a way of not passing the whole string around (in the
5756
// ID) in order to find a substring.
58-
final fullString = stringFromDartId(remoteObject.objectId);
57+
final objectId = remoteObject?.objectId;
58+
if (objectId == null) return null;
59+
final fullString = stringFromDartId(objectId);
5960
var preview = fullString;
6061
var truncated = false;
6162
if (offset != null || count != null) {
@@ -76,10 +77,12 @@ class InstanceHelper extends Domain {
7677
..offset = (truncated ? offset : null);
7778
}
7879

79-
Future<Instance> _closureInstanceFor(RemoteObject remoteObject) async {
80+
Future<Instance?> _closureInstanceFor(RemoteObject remoteObject) async {
81+
final objectId = remoteObject.objectId;
82+
if (objectId == null) return null;
8083
final result = Instance(
8184
kind: InstanceKind.kClosure,
82-
id: remoteObject.objectId,
85+
id: objectId,
8386
identityHashCode: remoteObject.objectId.hashCode,
8487
classRef: classRefForClosure);
8588
return result;
@@ -90,27 +93,31 @@ class InstanceHelper extends Domain {
9093
/// Does a remote eval to get instance information. Returns null if there
9194
/// isn't a corresponding instance. For enumerable objects, [offset] and
9295
/// [count] allow retrieving a sub-range of properties.
93-
Future<Instance> instanceFor(RemoteObject remoteObject,
94-
{int offset, int count}) async {
96+
Future<Instance?> instanceFor(RemoteObject? remoteObject,
97+
{int? offset, int? count}) async {
9598
final primitive = _primitiveInstanceOrNull(remoteObject, offset, count);
9699
if (primitive != null) {
97100
return primitive;
98101
}
102+
final objectId = remoteObject?.objectId;
103+
if (remoteObject == null || objectId == null) return null;
99104

100105
// TODO: This is checking the JS object ID for the dart pattern we use for
101106
// VM objects, which seems wrong (and, we catch 'string' types above).
102-
if (isStringId(remoteObject.objectId)) {
107+
if (isStringId(objectId)) {
103108
return _stringInstanceFor(remoteObject, offset, count);
104109
}
105110

106111
final metaData = await ClassMetaData.metaDataFor(
107112
inspector.remoteDebugger, remoteObject, inspector);
108-
final classRef = metaData.classRef;
113+
114+
final classRef = metaData?.classRef;
115+
if (metaData == null || classRef == null) return null;
109116
if (metaData.jsName == 'Function') {
110117
return _closureInstanceFor(remoteObject);
111118
}
112119

113-
final properties = await debugger.getProperties(remoteObject.objectId,
120+
final properties = await debugger.getProperties(objectId,
114121
offset: offset, count: count, length: metaData.length);
115122
if (metaData.isSystemList) {
116123
return await _listInstanceFor(
@@ -125,8 +132,8 @@ class InstanceHelper extends Domain {
125132

126133
/// If [remoteObject] represents a primitive, return an [Instance] for it,
127134
/// otherwise return null.
128-
Instance _primitiveInstanceOrNull(
129-
RemoteObject remoteObject, int offset, int count) {
135+
Instance? _primitiveInstanceOrNull(
136+
RemoteObject? remoteObject, int? offset, int? count) {
130137
switch (remoteObject?.type ?? 'undefined') {
131138
case 'string':
132139
return _stringInstanceFor(remoteObject, offset, count);
@@ -150,7 +157,7 @@ class InstanceHelper extends Domain {
150157
name: property.name,
151158
declaredType: InstanceRef(
152159
kind: InstanceKind.kType,
153-
classRef: instance.classRef,
160+
classRef: instance?.classRef,
154161
identityHashCode: createId().hashCode,
155162
id: createId()),
156163
owner: classRef,
@@ -165,27 +172,37 @@ class InstanceHelper extends Domain {
165172

166173
/// Create a plain instance of [classRef] from [remoteObject] and the JS
167174
/// properties [properties].
168-
Future<Instance> _plainInstanceFor(ClassRef classRef,
175+
Future<Instance?> _plainInstanceFor(ClassRef classRef,
169176
RemoteObject remoteObject, List<Property> properties) async {
177+
final objectId = remoteObject.objectId;
178+
if (objectId == null) return null;
170179
final dartProperties = await _dartFieldsFor(properties, remoteObject);
171180
var boundFields = await Future.wait(
172181
dartProperties.map<Future<BoundField>>((p) => _fieldFor(p, classRef)));
173182
boundFields = boundFields
174-
.where((bv) => bv != null && !isNativeJsObject(bv.value as InstanceRef))
183+
.where((bv) => !isNativeJsObject(bv.value as InstanceRef))
175184
.toList()
176-
..sort((a, b) => a.decl.name.compareTo(b.decl.name));
185+
..sort(_compareBoundFields);
177186
final result = Instance(
178187
kind: InstanceKind.kPlainInstance,
179-
id: remoteObject.objectId,
188+
id: objectId,
180189
identityHashCode: remoteObject.objectId.hashCode,
181190
classRef: classRef)
182191
..fields = boundFields;
183192
return result;
184193
}
185194

195+
int _compareBoundFields(BoundField a, BoundField b) {
196+
final aName = a.decl?.name;
197+
final bName = b.decl?.name;
198+
if (aName == null) return bName == null ? 0 : -1;
199+
if (bName == null) return 1;
200+
return aName.compareTo(bName);
201+
}
202+
186203
/// The associations for a Dart Map or IdentityMap.
187204
Future<List<MapAssociation>> _mapAssociations(
188-
RemoteObject map, int offset, int count) async {
205+
RemoteObject map, int? offset, int? count) async {
189206
// We do this in in awkward way because we want the keys and values, but we
190207
// can't return things by value or some Dart objects will come back as
191208
// values that we need to be RemoteObject, e.g. a List of int.
@@ -213,25 +230,34 @@ class InstanceHelper extends Domain {
213230
final valuesInstance =
214231
await instanceFor(values, offset: offset, count: count);
215232
final associations = <MapAssociation>[];
216-
Map.fromIterables(keysInstance.elements, valuesInstance.elements)
217-
.forEach((key, value) {
218-
associations.add(MapAssociation(key: key, value: value));
219-
});
233+
final keyElements = keysInstance?.elements;
234+
final valueElements = valuesInstance?.elements;
235+
if (keyElements != null && valueElements != null) {
236+
Map.fromIterables(keyElements, valueElements).forEach((key, value) {
237+
associations.add(MapAssociation(key: key, value: value));
238+
});
239+
}
220240
return associations;
221241
}
222242

223243
/// Create a Map instance with class [classRef] from [remoteObject].
224-
Future<Instance> _mapInstanceFor(ClassRef classRef, RemoteObject remoteObject,
225-
List<Property> _, int offset, int count) async {
244+
Future<Instance?> _mapInstanceFor(
245+
ClassRef classRef,
246+
RemoteObject remoteObject,
247+
List<Property> _,
248+
int? offset,
249+
int? count) async {
250+
final objectId = remoteObject.objectId;
251+
if (objectId == null) return null;
226252
// Maps are complicated, do an eval to get keys and values.
227253
final associations = await _mapAssociations(remoteObject, offset, count);
228254
final length = (offset == null && count == null)
229255
? associations.length
230-
: (await instanceRefFor(remoteObject)).length;
256+
: (await instanceRefFor(remoteObject))?.length;
231257
return Instance(
232258
identityHashCode: remoteObject.objectId.hashCode,
233259
kind: InstanceKind.kMap,
234-
id: remoteObject.objectId,
260+
id: objectId,
235261
classRef: classRef)
236262
..length = length
237263
..offset = offset
@@ -241,24 +267,26 @@ class InstanceHelper extends Domain {
241267

242268
/// Create a List instance of [classRef] from [remoteObject] with the JS
243269
/// properties [properties].
244-
Future<Instance> _listInstanceFor(
270+
Future<Instance?> _listInstanceFor(
245271
ClassRef classRef,
246272
RemoteObject remoteObject,
247273
List<Property> properties,
248-
int offset,
249-
int count) async {
250-
final numberOfProperties = _lengthOf(properties);
274+
int? offset,
275+
int? count) async {
276+
final objectId = remoteObject.objectId;
277+
if (objectId == null) return null;
278+
final numberOfProperties = _lengthOf(properties) ?? 0;
251279
final length = (offset == null && count == null)
252280
? numberOfProperties
253-
: (await instanceRefFor(remoteObject)).length;
254-
final indexed =
255-
properties.sublist(0, min(count ?? length, numberOfProperties));
281+
: (await instanceRefFor(remoteObject))?.length;
282+
final indexed = properties.sublist(
283+
0, min(count ?? length ?? numberOfProperties, numberOfProperties));
256284
final fields = await Future.wait(indexed
257285
.map((property) async => await _instanceRefForRemote(property.value)));
258286
return Instance(
259287
identityHashCode: remoteObject.objectId.hashCode,
260288
kind: InstanceKind.kList,
261-
id: remoteObject.objectId,
289+
id: objectId,
262290
classRef: classRef)
263291
..length = length
264292
..elements = fields
@@ -271,9 +299,9 @@ class InstanceHelper extends Domain {
271299
/// This is only applicable to Lists or Maps, where we expect a length
272300
/// attribute. Even if a plain instance happens to have a length field, we
273301
/// don't use it to determine the properties to display.
274-
int _lengthOf(List<Property> properties) {
302+
int? _lengthOf(List<Property> properties) {
275303
final lengthProperty = properties.firstWhere((p) => p.name == 'length');
276-
return lengthProperty.value.value as int;
304+
return lengthProperty.value?.value as int?;
277305
}
278306

279307
/// Filter [allJsProperties] and return a list containing only those
@@ -326,15 +354,15 @@ class InstanceHelper extends Domain {
326354
/// Create an InstanceRef for an object, which may be a RemoteObject, or may
327355
/// be something returned by value from Chrome, e.g. number, boolean, or
328356
/// String.
329-
Future<InstanceRef> instanceRefFor(Object value) {
357+
Future<InstanceRef?> instanceRefFor(Object value) {
330358
final remote = value is RemoteObject
331359
? value
332360
: RemoteObject({'value': value, 'type': _chromeType(value)});
333361
return _instanceRefForRemote(remote);
334362
}
335363

336364
/// The Chrome type for a value.
337-
String _chromeType(Object value) {
365+
String? _chromeType(Object? value) {
338366
if (value == null) return null;
339367
if (value is String) return 'string';
340368
if (value is num) return 'number';
@@ -344,15 +372,15 @@ class InstanceHelper extends Domain {
344372
}
345373

346374
/// Create an [InstanceRef] for the given Chrome [remoteObject].
347-
Future<InstanceRef> _instanceRefForRemote(RemoteObject remoteObject) async {
375+
Future<InstanceRef?> _instanceRefForRemote(RemoteObject? remoteObject) async {
348376
// If we have a null result, treat it as a reference to null.
349377
if (remoteObject == null) {
350378
return kNullInstanceRef;
351379
}
352380

353381
switch (remoteObject.type) {
354382
case 'string':
355-
final stringValue = remoteObject.value as String;
383+
final stringValue = remoteObject.value as String?;
356384
// TODO: Support truncation for long strings.
357385
// TODO(#777): dartIdFor() will return an ID containing the entire
358386
// string, even if we're truncating the string value here.
@@ -362,15 +390,16 @@ class InstanceHelper extends Domain {
362390
classRef: classRefForString,
363391
kind: InstanceKind.kString)
364392
..valueAsString = stringValue
365-
..length = stringValue.length;
393+
..length = stringValue?.length;
366394
case 'number':
367395
return _primitiveInstanceRef(InstanceKind.kDouble, remoteObject);
368396
case 'boolean':
369397
return _primitiveInstanceRef(InstanceKind.kBool, remoteObject);
370398
case 'undefined':
371399
return _primitiveInstanceRef(InstanceKind.kNull, remoteObject);
372400
case 'object':
373-
if (remoteObject.objectId == null) {
401+
final objectId = remoteObject.objectId;
402+
if (objectId == null) {
374403
return _primitiveInstanceRef(InstanceKind.kNull, remoteObject);
375404
}
376405
final metaData = await ClassMetaData.metaDataFor(
@@ -379,30 +408,34 @@ class InstanceHelper extends Domain {
379408
if (metaData.isSystemList) {
380409
return InstanceRef(
381410
kind: InstanceKind.kList,
382-
id: remoteObject.objectId,
411+
id: objectId,
383412
identityHashCode: remoteObject.objectId.hashCode,
384413
classRef: metaData.classRef)
385414
..length = metaData.length;
386415
}
387416
if (metaData.isSystemMap) {
388417
return InstanceRef(
389418
kind: InstanceKind.kMap,
390-
id: remoteObject.objectId,
419+
id: objectId,
391420
identityHashCode: remoteObject.objectId.hashCode,
392421
classRef: metaData.classRef)
393422
..length = metaData.length;
394423
}
395424
return InstanceRef(
396425
kind: InstanceKind.kPlainInstance,
397-
id: remoteObject.objectId,
426+
id: objectId,
398427
identityHashCode: remoteObject.objectId.hashCode,
399428
classRef: metaData.classRef);
400429
case 'function':
401430
final functionMetaData = await FunctionMetaData.metaDataFor(
402431
inspector.remoteDebugger, remoteObject);
432+
final objectId = remoteObject.objectId;
433+
if (objectId == null) {
434+
return _primitiveInstanceRef(InstanceKind.kNull, remoteObject);
435+
}
403436
return InstanceRef(
404437
kind: InstanceKind.kClosure,
405-
id: remoteObject.objectId,
438+
id: objectId,
406439
identityHashCode: remoteObject.objectId.hashCode,
407440
classRef: classRefForClosure)
408441
// TODO(grouma) - fill this in properly.

0 commit comments

Comments
 (0)