@@ -680,11 +680,39 @@ typedef InspectorSelectionChangedCallback = void Function();
680
680
681
681
/// Structure to help reference count Dart objects referenced by a GUI tool
682
682
/// using [WidgetInspectorService] .
683
- class _InspectorReferenceData {
684
- _InspectorReferenceData (this .object);
683
+ ///
684
+ /// Does not hold the object from garbage collection.
685
+ @visibleForTesting
686
+ class InspectorReferenceData {
687
+ /// Creates an instance of [InspectorReferenceData] .
688
+ InspectorReferenceData (Object object, this .id) {
689
+ // These types are not supported by [WeakReference].
690
+ // See https://api.dart.dev/stable/3.0.2/dart-core/WeakReference-class.html
691
+ if (object is String || object is num || object is bool ) {
692
+ _value = object;
693
+ return ;
694
+ }
695
+
696
+ _ref = WeakReference <Object >(object);
697
+ }
698
+
699
+ WeakReference <Object >? _ref;
700
+
701
+ Object ? _value;
702
+
703
+ /// The id of the object in the widget inspector records.
704
+ final String id;
685
705
686
- final Object object;
706
+ /// The number of times the object has been referenced.
687
707
int count = 1 ;
708
+
709
+ /// The value.
710
+ Object ? get value {
711
+ if (_ref != null ) {
712
+ return _ref! .target;
713
+ }
714
+ return _value;
715
+ }
688
716
}
689
717
690
718
// Production implementation of [WidgetInspectorService].
@@ -742,9 +770,9 @@ mixin WidgetInspectorService {
742
770
/// The VM service protocol does not keep alive object references so this
743
771
/// class needs to manually manage groups of objects that should be kept
744
772
/// alive.
745
- final Map <String , Set <_InspectorReferenceData >> _groups = < String , Set <_InspectorReferenceData >> {};
746
- final Map <String , _InspectorReferenceData > _idToReferenceData = < String , _InspectorReferenceData > {};
747
- final Map <Object , String > _objectToId = Map <Object , String >. identity ();
773
+ final Map <String , Set <InspectorReferenceData >> _groups = < String , Set <InspectorReferenceData >> {};
774
+ final Map <String , InspectorReferenceData > _idToReferenceData = < String , InspectorReferenceData > {};
775
+ final WeakMap <Object , String > _objectToId = WeakMap <Object , String >();
748
776
int _nextId = 0 ;
749
777
750
778
/// The pubRootDirectories that are currently configured for the widget inspector.
@@ -1270,20 +1298,22 @@ mixin WidgetInspectorService {
1270
1298
/// references from a different group.
1271
1299
@protected
1272
1300
void disposeGroup (String name) {
1273
- final Set <_InspectorReferenceData >? references = _groups.remove (name);
1301
+ final Set <InspectorReferenceData >? references = _groups.remove (name);
1274
1302
if (references == null ) {
1275
1303
return ;
1276
1304
}
1277
1305
references.forEach (_decrementReferenceCount);
1278
1306
}
1279
1307
1280
- void _decrementReferenceCount (_InspectorReferenceData reference) {
1308
+ void _decrementReferenceCount (InspectorReferenceData reference) {
1281
1309
reference.count -= 1 ;
1282
1310
assert (reference.count >= 0 );
1283
1311
if (reference.count == 0 ) {
1284
- final String ? id = _objectToId.remove (reference.object);
1285
- assert (id != null );
1286
- _idToReferenceData.remove (id);
1312
+ final Object ? value = reference.value;
1313
+ if (value != null ) {
1314
+ _objectToId.remove (value);
1315
+ }
1316
+ _idToReferenceData.remove (reference.id);
1287
1317
}
1288
1318
}
1289
1319
@@ -1295,14 +1325,16 @@ mixin WidgetInspectorService {
1295
1325
return null ;
1296
1326
}
1297
1327
1298
- final Set <_InspectorReferenceData > group = _groups.putIfAbsent (groupName, () => Set <_InspectorReferenceData >.identity ());
1328
+ final Set <InspectorReferenceData > group = _groups.putIfAbsent (groupName, () => Set <InspectorReferenceData >.identity ());
1299
1329
String ? id = _objectToId[object];
1300
- _InspectorReferenceData referenceData;
1330
+ InspectorReferenceData referenceData;
1301
1331
if (id == null ) {
1332
+ // TODO(polina-c): comment here why we increase memory footprint by the prefix 'inspector-'.
1333
+ // https://github.com/flutter/devtools/issues/5995
1302
1334
id = 'inspector-$_nextId ' ;
1303
1335
_nextId += 1 ;
1304
1336
_objectToId[object] = id;
1305
- referenceData = _InspectorReferenceData (object);
1337
+ referenceData = InspectorReferenceData (object, id );
1306
1338
_idToReferenceData[id] = referenceData;
1307
1339
group.add (referenceData);
1308
1340
} else {
@@ -1332,11 +1364,11 @@ mixin WidgetInspectorService {
1332
1364
return null ;
1333
1365
}
1334
1366
1335
- final _InspectorReferenceData ? data = _idToReferenceData[id];
1367
+ final InspectorReferenceData ? data = _idToReferenceData[id];
1336
1368
if (data == null ) {
1337
1369
throw FlutterError .fromParts (< DiagnosticsNode > [ErrorSummary ('Id does not exist.' )]);
1338
1370
}
1339
- return data.object ;
1371
+ return data.value ;
1340
1372
}
1341
1373
1342
1374
/// Returns the object to introspect to determine the source location of an
@@ -1368,7 +1400,7 @@ mixin WidgetInspectorService {
1368
1400
return ;
1369
1401
}
1370
1402
1371
- final _InspectorReferenceData ? referenceData = _idToReferenceData[id];
1403
+ final InspectorReferenceData ? referenceData = _idToReferenceData[id];
1372
1404
if (referenceData == null ) {
1373
1405
throw FlutterError .fromParts (< DiagnosticsNode > [ErrorSummary ('Id does not exist' )]);
1374
1406
}
@@ -3731,3 +3763,54 @@ class _WidgetFactory {
3731
3763
// recognize the annotation.
3732
3764
// ignore: library_private_types_in_public_api
3733
3765
const _WidgetFactory widgetFactory = _WidgetFactory ();
3766
+
3767
+ /// Does not hold keys from garbage collection.
3768
+ @visibleForTesting
3769
+ class WeakMap <K , V > {
3770
+ Expando <Object > _objects = Expando <Object >();
3771
+
3772
+ /// Strings, numbers, booleans.
3773
+ final Map <K , V > _primitives = < K , V > {};
3774
+
3775
+ bool _isPrimitive (Object ? key) {
3776
+ return key == null || key is String || key is num || key is bool ;
3777
+ }
3778
+
3779
+ /// Returns the value for the given [key] or null if [key] is not in the map
3780
+ /// or garbage collected.
3781
+ ///
3782
+ /// Does not support records to act as keys.
3783
+ V ? operator [](K key){
3784
+ if (_isPrimitive (key)) {
3785
+ return _primitives[key];
3786
+ } else {
3787
+ return _objects[key! ] as V ? ;
3788
+ }
3789
+ }
3790
+
3791
+ /// Associates the [key] with the given [value] .
3792
+ void operator []= (K key, V value){
3793
+ if (_isPrimitive (key)) {
3794
+ _primitives[key] = value;
3795
+ } else {
3796
+ _objects[key! ] = value;
3797
+ }
3798
+ }
3799
+
3800
+ /// Removes the value for the given [key] from the map.
3801
+ V ? remove (K key) {
3802
+ if (_isPrimitive (key)) {
3803
+ return _primitives.remove (key);
3804
+ } else {
3805
+ final V ? result = _objects[key! ] as V ? ;
3806
+ _objects[key] = null ;
3807
+ return result;
3808
+ }
3809
+ }
3810
+
3811
+ /// Removes all pairs from the map.
3812
+ void clear () {
3813
+ _objects = Expando <Object >();
3814
+ _primitives.clear ();
3815
+ }
3816
+ }
0 commit comments