Add support for inspecting instance of WeakArray (#5316)
Also refactored VmSubtypeTestCacheDisplay into a more generic VmSimpleListDisplay.
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
index 039076e..8b992f2 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_inspector_view_controller.dart
@@ -179,6 +179,10 @@
object = SubtypeTestCacheObject(
ref: objRef,
);
+ } else if (objRef.isWeakArray) {
+ object = WeakArrayObject(
+ ref: objRef,
+ );
}
await object?.initialize();
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
index bad3a4c..6b03929 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/object_viewport.dart
@@ -19,7 +19,7 @@
import 'vm_object_model.dart';
import 'vm_object_pool_display.dart';
import 'vm_script_display.dart';
-import 'vm_subtype_test_cache_display.dart';
+import 'vm_simple_list_display.dart';
/// Displays the VM information for the currently selected object in the
/// program explorer.
@@ -132,10 +132,10 @@
icData: obj,
);
}
- if (obj is SubtypeTestCacheObject) {
- return VmSubtypeTestCacheDisplay(
+ if (obj is VmListObject) {
+ return VmSimpleListDisplay(
controller: controller,
- subtypeTestCache: obj,
+ vmObject: obj,
);
}
return const SizedBox.shrink();
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
index 2ac7ce0..a41c0c7 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_instance_display.dart
@@ -196,7 +196,7 @@
),
onTap: onTap,
),
- if (variable.ref!.value is! Sentinel)
+ if (variable.ref!.value is! Sentinel && variable.ref!.value is ObjRef?)
VmServiceObjectLink(
object: variable.ref!.value as ObjRef?,
textBuilder: (object) {
@@ -208,6 +208,11 @@
},
onTap: controller.findAndSelectNodeForObject,
)
+ else
+ Text(
+ variable.ref!.value.toString(),
+ style: Theme.of(context).subtleFixedFontStyle,
+ )
],
);
}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
index 668fe7c..0bccf4e 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_object_model.dart
@@ -106,6 +106,22 @@
}
}
+/// A class of [VmObject] for VM objects that simply provide a list of elements
+/// to be displayed.
+///
+/// All instances of [VmListObject] will be displaying using the
+/// [VmSimpleListDisplay] view.
+///
+/// Implementers of this interface must have a non-null return value for one of
+/// `elementsAsList` or `elementsAsInstance`.
+abstract class VmListObject extends VmObject {
+ VmListObject({required super.ref});
+
+ List<Response?>? get elementsAsList;
+
+ InstanceRef? get elementsAsInstance;
+}
+
/// Stores a 'Class' VM object and provides an interface for obtaining the
/// Dart VM information related to this object.
class ClassObject extends VmObject {
@@ -335,7 +351,7 @@
/// Stores a 'SubtypeTestCache' VM object and provides an interface for
/// obtaining the Dart VM information related to this object.
-class SubtypeTestCacheObject extends VmObject {
+class SubtypeTestCacheObject extends VmListObject {
SubtypeTestCacheObject({required super.ref});
@override
@@ -346,4 +362,31 @@
@override
SubtypeTestCache get obj => _obj.asSubtypeTestCache;
+
+ @override
+ InstanceRef get elementsAsInstance => obj.cache;
+
+ @override
+ List<Response?>? get elementsAsList => null;
+}
+
+/// Stores a 'WeakArray' VM object and provides an interface for
+/// obtaining the Dart VM information related to this object.
+class WeakArrayObject extends VmListObject {
+ WeakArrayObject({required super.ref});
+
+ @override
+ SourceLocation? get _sourceLocation => null;
+
+ @override
+ String? get name => null;
+
+ @override
+ WeakArray get obj => _obj.asWeakArray;
+
+ @override
+ InstanceRef? get elementsAsInstance => null;
+
+ @override
+ List<Response?>? get elementsAsList => obj.asWeakArray.elements;
}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart
new file mode 100644
index 0000000..8d23f2f
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart
@@ -0,0 +1,105 @@
+// Copyright 2023 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../../../shared/common_widgets.dart';
+import '../../../shared/globals.dart';
+import '../vm_developer_common_widgets.dart';
+import 'object_inspector_view_controller.dart';
+import 'vm_object_model.dart';
+
+/// A widget for the object inspector historyViewport displaying information
+/// related to list-like VM objects (e.g., subtype test cache, WeakArray, etc).
+class VmSimpleListDisplay<T extends VmListObject> extends StatefulWidget {
+ const VmSimpleListDisplay({
+ required this.controller,
+ required this.vmObject,
+ });
+
+ final ObjectInspectorViewController controller;
+ final T vmObject;
+
+ @override
+ State<VmSimpleListDisplay> createState() => _VmSimpleListDisplayState();
+}
+
+class _VmSimpleListDisplayState extends State<VmSimpleListDisplay> {
+ final entries = <Response?>[];
+
+ late Future<void> _initialized;
+
+ @override
+ void initState() {
+ super.initState();
+ _initialize();
+ }
+
+ @override
+ void didUpdateWidget(VmSimpleListDisplay oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (widget.vmObject == oldWidget.vmObject) {
+ return;
+ }
+ _initialize();
+ }
+
+ void _initialize() async {
+ entries.clear();
+ final elementsInstance = widget.vmObject.elementsAsInstance;
+ if (elementsInstance != null) {
+ if (elementsInstance is Instance) {
+ entries.addAll(elementsInstance.elements!.cast<Response?>());
+ _initialized = Future.value();
+ return;
+ }
+
+ final isolateId =
+ serviceManager.isolateManager.selectedIsolate.value!.id!;
+ final service = serviceManager.service!;
+ _initialized = service.getObject(isolateId, elementsInstance.id!).then(
+ (e) => entries.addAll((e as Instance).elements!.cast<Response?>()),
+ );
+ return;
+ }
+ final elementsList = widget.vmObject.elementsAsList;
+ assert(
+ elementsList != null,
+ 'One of elementsAsList or elementsAsInstance must be non-null',
+ );
+ entries.addAll(elementsList!);
+ _initialized = Future.value();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder<void>(
+ future: _initialized,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState != ConnectionState.done)
+ return const CenteredCircularProgressIndicator();
+ return VmObjectDisplayBasicLayout(
+ controller: widget.controller,
+ object: widget.vmObject,
+ generalDataRows: [
+ ...vmObjectGeneralDataRows(
+ widget.controller,
+ widget.vmObject,
+ ),
+ ],
+ expandableWidgets: [
+ ExpansionTileInstanceList(
+ controller: widget.controller,
+ title: 'Entries',
+ elements: entries,
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_subtype_test_cache_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_subtype_test_cache_display.dart
deleted file mode 100644
index 7bb7d8a..0000000
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_inspector/vm_subtype_test_cache_display.dart
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2023 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-
-import 'package:flutter/material.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../../shared/common_widgets.dart';
-import '../../../shared/globals.dart';
-import '../vm_developer_common_widgets.dart';
-import 'object_inspector_view_controller.dart';
-import 'vm_object_model.dart';
-
-/// A widget for the object inspector historyViewport displaying information
-/// related to ICData objects in the Dart VM.
-class VmSubtypeTestCacheDisplay extends StatefulWidget {
- const VmSubtypeTestCacheDisplay({
- required this.controller,
- required this.subtypeTestCache,
- });
-
- final ObjectInspectorViewController controller;
- final SubtypeTestCacheObject subtypeTestCache;
-
- @override
- State<VmSubtypeTestCacheDisplay> createState() =>
- _VmSubtypeTestCacheDisplayState();
-}
-
-class _VmSubtypeTestCacheDisplayState extends State<VmSubtypeTestCacheDisplay> {
- final entries = <ObjRef?>[];
-
- late Future<void> _initialized;
-
- @override
- void initState() {
- super.initState();
- _initialize();
- }
-
- @override
- void didUpdateWidget(VmSubtypeTestCacheDisplay oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (widget.subtypeTestCache == oldWidget.subtypeTestCache) {
- return;
- }
- _initialize();
- }
-
- void _initialize() async {
- entries.clear();
-
- final subtypeTestCache = widget.subtypeTestCache.obj;
- final cache = subtypeTestCache.cache;
- if (cache is Instance) {
- entries.addAll(cache.elements!.cast<ObjRef?>());
- _initialized = Future.value();
- return;
- }
-
- final isolateId = serviceManager.isolateManager.selectedIsolate.value!.id!;
- final service = serviceManager.service!;
- _initialized = service
- .getObject(isolateId, cache.id!)
- .then((e) => entries.addAll((e as Instance).elements!.cast<ObjRef?>()));
- }
-
- @override
- Widget build(BuildContext context) {
- return FutureBuilder<void>(
- future: _initialized,
- builder: (context, snapshot) {
- if (snapshot.connectionState != ConnectionState.done)
- return const CenteredCircularProgressIndicator();
- return VmObjectDisplayBasicLayout(
- controller: widget.controller,
- object: widget.subtypeTestCache,
- generalDataRows: [
- ...vmObjectGeneralDataRows(
- widget.controller,
- widget.subtypeTestCache,
- ),
- ],
- expandableWidgets: [
- ExpansionTileInstanceList(
- controller: widget.controller,
- title: 'Entries',
- elements: entries,
- ),
- ],
- );
- },
- );
- }
-}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
index 46fe3b3..42558f4 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
@@ -381,7 +381,7 @@
final ObjectInspectorViewController controller;
final String title;
- final List<ObjRef?> elements;
+ final List<Response?> elements;
@override
Widget build(BuildContext context) {
@@ -810,6 +810,9 @@
object is ObjRef &&
object.isSubtypeTestCache) {
text = 'SubtypeTestCache';
+ } else if (object != null && object is ObjRef && object.isWeakArray) {
+ final weakArray = object.asWeakArray;
+ text = 'WeakArray(length: ${weakArray.length})';
}
return text;
}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
index cc1059b..7d5ba96 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
@@ -158,6 +158,7 @@
static const _icDataType = 'ICData';
static const _objectPoolType = 'ObjectPool';
static const _subtypeTestCache = 'SubtypeTestCache';
+ static const _weakArrayType = 'WeakArray';
/// The internal type of the object.
///
@@ -183,6 +184,12 @@
/// Casts the current [ObjRef] into an instance of [SubtypeTestCacheRef].
SubtypeTestCacheRef get asSubtypeTestCache =>
SubtypeTestCacheRef.parse(json!);
+
+ /// `true` if this object is an instance of [WeakArrayRef].
+ bool get isWeakArray => vmType == _weakArrayType;
+
+ /// Casts the current [ObjRef] into an instance of [WeakArrayRef].
+ WeakArrayRef get asWeakArray => WeakArrayRef.parse(json!);
}
/// An extension on [Obj] which allows for access to VM internal fields.
@@ -195,6 +202,87 @@
/// Casts the current [Obj] into an instance of [SubtypeTestCache].
SubtypeTestCache get asSubtypeTestCache => SubtypeTestCache.parse(json!);
+
+ /// Casts the current [Obj] into an instance of [WeakArray].
+ WeakArray get asWeakArray => WeakArray.parse(json!);
+}
+
+/// A reference to a [WeakArray], which is an array consisting of weak
+/// persistent handles.
+///
+/// References to an object from a [WeakArray] are ignored by the GC and will
+/// not prevent referenced objects from being collected when all other
+/// references to the object disappear.
+class WeakArrayRef implements ObjRef {
+ WeakArrayRef({
+ required this.id,
+ required this.json,
+ required this.length,
+ });
+
+ factory WeakArrayRef.parse(Map<String, dynamic> json) => WeakArrayRef(
+ id: json['id'],
+ json: json,
+ length: json['length'],
+ );
+
+ @override
+ bool? fixedId;
+
+ @override
+ String? id;
+
+ @override
+ Map<String, dynamic>? json;
+
+ final int length;
+
+ @override
+ Map<String, dynamic> toJson() => json!;
+
+ @override
+ String get type => 'WeakArray';
+}
+
+/// A populated representation of a [WeakArray], which is an array consisting
+/// of weak persistent handles.
+///
+/// References to an object from a [WeakArray] are ignored by the GC and will
+/// not prevent referenced objects from being collected when all other
+/// references to the object disappear.
+class WeakArray extends WeakArrayRef implements Obj {
+ WeakArray({
+ required super.id,
+ required super.json,
+ required super.length,
+ required this.elements,
+ required this.size,
+ required this.classRef,
+ });
+
+ factory WeakArray.parse(Map<String, dynamic> json) => WeakArray(
+ id: json['id'],
+ json: json,
+ length: json['length'],
+ size: json['size'],
+ elements: (createServiceObject(json['elements'], []) as List)
+ .cast<Response?>(),
+ classRef: createServiceObject(json['class'], [])! as ClassRef,
+ );
+
+ final List<Response?> elements;
+
+ @override
+ Map<String, dynamic> toJson() => json!;
+
+ @override
+ String get type => 'WeakArray';
+
+ @override
+ ClassRef? classRef;
+
+ @override
+ int? size;
}
/// A partially-populated representation of the Dart VM's subtype test cache.
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/vm_subtype_test_cache_display_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/vm_simple_list_display_test.dart
similarity index 89%
rename from packages/devtools_app/test/vm_developer/object_inspector/vm_subtype_test_cache_display_test.dart
rename to packages/devtools_app/test/vm_developer/object_inspector/vm_simple_list_display_test.dart
index bb8a1ee..e52a6f9 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/vm_subtype_test_cache_display_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/vm_simple_list_display_test.dart
@@ -3,7 +3,7 @@
// found in the LICENSE file.
import 'package:devtools_app/devtools_app.dart';
-import 'package:devtools_app/src/screens/vm_developer/object_inspector/vm_subtype_test_cache_display.dart';
+import 'package:devtools_app/src/screens/vm_developer/object_inspector/vm_simple_list_display.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_developer_common_widgets.dart';
import 'package:devtools_test/devtools_test.dart';
import 'package:flutter/material.dart';
@@ -27,15 +27,17 @@
mockSubtypeTestCacheObject = MockSubtypeTestCacheObject();
mockVmObject(mockSubtypeTestCacheObject);
+ final cache = Instance(id: 'inst-2', length: 0, elements: []);
when(mockSubtypeTestCacheObject.obj).thenReturn(
SubtypeTestCache(
id: 'subtype-test-cache-id',
size: 64,
- cache: Instance(id: 'inst-2', length: 0, elements: []),
+ cache: cache,
classRef: ClassRef(id: 'cls-id-2', name: 'SubtypeTestCache'),
json: {},
),
);
+ when(mockSubtypeTestCacheObject.elementsAsInstance).thenReturn(cache);
});
group('Subtype test cache display test', () {
@@ -45,8 +47,8 @@
(WidgetTester tester) async {
await tester.pumpWidget(
wrap(
- VmSubtypeTestCacheDisplay(
- subtypeTestCache: mockSubtypeTestCacheObject,
+ VmSimpleListDisplay(
+ vmObject: mockSubtypeTestCacheObject,
controller: ObjectInspectorViewController(),
),
),