From fca7813650e18845cd4d5e7f4a57cf40c4a48d7e Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Thu, 10 Nov 2022 17:50:51 +0000 Subject: [PATCH] [ Observatory ] Add basic records support to Observatory Fixes https://github.com/dart-lang/sdk/issues/50405 TEST=Manual testing Change-Id: If14f434792c89e3509895fcdd17561df810798e3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268581 Commit-Queue: Ben Konyi Reviewed-by: Derek Xu --- .../lib/src/elements/helpers/any_ref.dart | 2 ++ .../lib/src/elements/instance_ref.dart | 22 ++++++++++---- .../lib/src/elements/instance_view.dart | 9 ++++-- .../lib/src/models/objects/instance.dart | 15 +++++++++- .../observatory/lib/src/service/object.dart | 30 ++++++++++++------- runtime/observatory/tests/ui/inspector.dart | 25 ++++++++++++++-- .../observatory/tests/ui/inspector_part.dart | 2 ++ .../lib/src/elements/helpers/any_ref.dart | 2 ++ .../lib/src/elements/instance_ref.dart | 21 +++++++++---- .../lib/src/elements/instance_view.dart | 9 ++++-- .../lib/src/models/objects/instance.dart | 15 +++++++++- .../observatory_2/lib/src/service/object.dart | 28 +++++++++++------ runtime/vm/object_service.cc | 4 +-- 13 files changed, 142 insertions(+), 42 deletions(-) diff --git a/runtime/observatory/lib/src/elements/helpers/any_ref.dart b/runtime/observatory/lib/src/elements/helpers/any_ref.dart index ac5e1e38443..222f40d4736 100644 --- a/runtime/observatory/lib/src/elements/helpers/any_ref.dart +++ b/runtime/observatory/lib/src/elements/helpers/any_ref.dart @@ -93,6 +93,8 @@ Element anyRef(M.IsolateRef isolate, ref, M.ObjectRepository objects, } } else if (ref is M.Sentinel) { return new SentinelValueElement(ref, queue: queue).element; + } else if (ref is num || ref is String) { + return new SpanElement()..text = ref.toString(); } throw new Exception('Unknown ref type (${ref.runtimeType})'); } diff --git a/runtime/observatory/lib/src/elements/instance_ref.dart b/runtime/observatory/lib/src/elements/instance_ref.dart index 819a499c99f..01ba0330212 100644 --- a/runtime/observatory/lib/src/elements/instance_ref.dart +++ b/runtime/observatory/lib/src/elements/instance_ref.dart @@ -128,6 +128,7 @@ class InstanceRefElement extends CustomElement implements Renderable { case M.InstanceKind.functionType: case M.InstanceKind.typeRef: case M.InstanceKind.typeParameter: + case M.InstanceKind.recordType: return [ new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) ..text = _instance.name @@ -194,12 +195,10 @@ class InstanceRefElement extends CustomElement implements Renderable { ] ]; case M.InstanceKind.mirrorReference: - return [ - new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) - ..classes = ['emphasize'] - ..text = _instance.clazz!.name - ]; case M.InstanceKind.weakProperty: + case M.InstanceKind.finalizer: + case M.InstanceKind.weakReference: + case M.InstanceKind.record: return [ new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) ..classes = ['emphasize'] @@ -216,6 +215,7 @@ class InstanceRefElement extends CustomElement implements Renderable { case M.InstanceKind.mirrorReference: case M.InstanceKind.stackTrace: case M.InstanceKind.weakProperty: + case M.InstanceKind.recordType: return true; case M.InstanceKind.list: case M.InstanceKind.map: @@ -339,6 +339,18 @@ class InstanceRefElement extends CustomElement implements Renderable { queue: _r.queue) .element, ]; + case M.InstanceKind.recordType: + final fields = _loadedInstance!.fields!.toList(); + return [ + for (int i = 0; i < fields.length; ++i) ...[ + new SpanElement()..text = '${fields[i].name} = ', + new InstanceRefElement( + _isolate, fields[i].value!.asValue!, _objects, + queue: _r.queue) + .element, + if (i + 1 != fields.length) new BRElement(), + ] + ]; default: return []; } diff --git a/runtime/observatory/lib/src/elements/instance_view.dart b/runtime/observatory/lib/src/elements/instance_view.dart index 2063c281800..4c4d69064a1 100644 --- a/runtime/observatory/lib/src/elements/instance_view.dart +++ b/runtime/observatory/lib/src/elements/instance_view.dart @@ -335,9 +335,12 @@ class InstanceViewElement extends CustomElement implements Renderable { ..content = [ new DivElement() ..classes = ['memberList'] - ..children = fields - .map((f) => member(f.decl, f.value)) - .toList() + ..children = fields.map((f) { + final name = _instance.kind == M.InstanceKind.record + ? f.name + : f.decl; + return member(name, f.value); + }).toList() ]) .element ] diff --git a/runtime/observatory/lib/src/models/objects/instance.dart b/runtime/observatory/lib/src/models/objects/instance.dart index 81020959067..1b2d58e2271 100644 --- a/runtime/observatory/lib/src/models/objects/instance.dart +++ b/runtime/observatory/lib/src/models/objects/instance.dart @@ -126,6 +126,18 @@ enum InstanceKind { /// An instance of the Dart class RawReceivePort receivePort, + + /// An instance of Record. + record, + + /// An instance of RecordType + recordType, + + /// An instance of Finalizer + finalizer, + + /// An instance of WeakReference + weakReference, } bool isTypedData(InstanceKind? kind) { @@ -463,7 +475,8 @@ abstract class Instance extends Object implements InstanceRef { abstract class BoundField { FieldRef? get decl; - Guarded? get value; + Guarded? get value; + dynamic get name; } abstract class NativeField { diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart index c4af948dc1c..1a256a6ad08 100644 --- a/runtime/observatory/lib/src/service/object.dart +++ b/runtime/observatory/lib/src/service/object.dart @@ -2108,7 +2108,7 @@ class ServiceMap extends ServiceObject _map.clear(); map.forEach((k, v) => _map[k] = v); - name = _map['name']; + name = _map['name']?.toString(); vmName = (_map.containsKey('_vmName') ? _map['_vmName'] : name); } @@ -2791,25 +2791,33 @@ M.InstanceKind stringToInstanceKind(String s) { return M.InstanceKind.typeRef; case 'ReceivePort': return M.InstanceKind.receivePort; + case '_RecordType': + return M.InstanceKind.recordType; + case '_Record': + return M.InstanceKind.record; + case 'Finalizer': + return M.InstanceKind.finalizer; + case 'WeakReference': + return M.InstanceKind.weakReference; } var message = 'Unrecognized instance kind: $s'; Logger.root.severe(message); throw new ArgumentError(message); } -class Guarded implements M.Guarded { +class Guarded implements M.Guarded { bool get isValue => asValue != null; bool get isSentinel => asSentinel != null; final Sentinel? asSentinel; final T? asValue; - factory Guarded(ServiceObject obj) { + factory Guarded(dynamic obj) { if (obj is Sentinel) { return new Guarded.fromSentinel(obj); } else if (obj is T) { return new Guarded.fromValue(obj); } - throw new Exception('${obj.type} is neither Sentinel or $T'); + throw new Exception('${obj.runtimeType} is neither Sentinel or $T'); } Guarded.fromSentinel(this.asSentinel) : asValue = null; @@ -2817,9 +2825,11 @@ class Guarded implements M.Guarded { } class BoundField implements M.BoundField { - final Field decl; - final Guarded value; - BoundField(this.decl, value) : value = new Guarded(value); + final Field? decl; + // String|int + final dynamic name; + final Guarded value; + BoundField(this.decl, this.name, value) : value = new Guarded(value); } class NativeField implements M.NativeField { @@ -2916,7 +2926,7 @@ class Instance extends HeapObject implements M.Instance { Instance._empty(ServiceObjectOwner? owner) : super._empty(owner); void _update(Map map, bool mapIsRef) { - // Extract full properties.1 + // Extract full properties. _upgradeCollection(map, isolate); super._update(map, mapIsRef); @@ -2925,7 +2935,7 @@ class Instance extends HeapObject implements M.Instance { // Coerce absence to false. valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true; closureFunction = map['closureFunction']; - name = map['name']; + name = map['name']?.toString(); length = map['length']; pattern = map['pattern']; typeClass = map['typeClass']; @@ -2958,7 +2968,7 @@ class Instance extends HeapObject implements M.Instance { if (map['fields'] != null) { var fields = []; for (var f in map['fields']) { - fields.add(new BoundField(f['decl'], f['value'])); + fields.add(new BoundField(f['decl'], f['name'], f['value'])); } this.fields = fields; } else { diff --git a/runtime/observatory/tests/ui/inspector.dart b/runtime/observatory/tests/ui/inspector.dart index e8fc90a4a7f..eeb461f37b7 100644 --- a/runtime/observatory/tests/ui/inspector.dart +++ b/runtime/observatory/tests/ui/inspector.dart @@ -2,6 +2,8 @@ // 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.19 + // See inspector.txt for expected behavior. library manual_inspector_test; @@ -43,10 +45,13 @@ class Node { var blockCopying; var blockFull; var blockFullWithChain; + var blockType; var boundedType; var capability; var counter; var expando; + var finalizer; + var finalizerEntry; var float32x4; var float64; var float64x2; @@ -62,7 +67,10 @@ class Node { var mirrorReference; var portReceive; var portSend; + var record; + var recordType; var regex; + late var sentinel; // Not initialized var smi; var stacktrace; var string; @@ -76,9 +84,12 @@ class Node { var theTrue; var type; var typeParameter; - var typedData; + var typedDataArray; + var typedDataView; + var typedDataUnmodifiableView; var userTag; var weakProperty; + var weakReference; genStackTrace() { try { @@ -141,16 +152,19 @@ class Node { array[0] = 1; array[1] = 2; array[2] = 3; - bigint = 1 << 65; + bigint = BigInt.one << 65; blockClean = genCleanBlock(); blockCopying = genCopyingBlock(); blockFull = genFullBlock(); blockFullWithChain = genFullBlockWithChain(); + blockType = blockClean.runtimeType; boundedType = extractPrivateField( reflect(new B()).type.typeVariables.single, '_reflectee'); counter = new Counter("CounterName", "Counter description"); expando = new Expando("expando-name"); expando[array] = 'The weakly associated value'; + finalizer = Finalizer((_){}); + finalizer.attach(this, this); float32x4 = new Float32x4(0.0, -1.0, 3.14, 2e28); float64 = 3.14; float64x2 = new Float64x2(0.0, 3.14); @@ -170,6 +184,8 @@ class Node { mirrorReference = extractPrivateField(mirrorClass, '_reflectee'); portReceive = new RawReceivePort(); portSend = portReceive.sendPort; + record = (1, 2, three: 3, four: 4); + recordType = record.runtimeType; regex = new RegExp("a*b+c"); smi = 7; stacktrace = genStackTrace(); @@ -185,10 +201,13 @@ class Node { type = String; typeParameter = extractPrivateField(reflectClass(A).typeVariables.single, '_reflectee'); - typedData = extractPrivateField(new ByteData(64), '_typedData'); + typedDataArray = Uint8List(32); + typedDataView = Uint8List.view(typedDataArray.buffer, 16); + typedDataUnmodifiableView = UnmodifiableUint8ListView(typedDataArray); userTag = new UserTag("Example tag name"); weakProperty = extractPrivateField(expando, '_data').firstWhere((e) => e != null); + weakReference = WeakReference(this); Isolate.spawn(secondMain, "Hello2").then((otherIsolate) { isolate = otherIsolate; diff --git a/runtime/observatory/tests/ui/inspector_part.dart b/runtime/observatory/tests/ui/inspector_part.dart index 06589e319f8..6222354a3ea 100644 --- a/runtime/observatory/tests/ui/inspector_part.dart +++ b/runtime/observatory/tests/ui/inspector_part.dart @@ -2,6 +2,8 @@ // 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.19 + part of manual_inspector_test; functionInPart() {} diff --git a/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart index 0ae29302c3b..a1f2d9617bc 100644 --- a/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart +++ b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart @@ -93,6 +93,8 @@ Element anyRef(M.IsolateRef isolate, ref, M.ObjectRepository objects, } } else if (ref is M.Sentinel) { return new SentinelValueElement(ref, queue: queue).element; + } else if (ref is num || ref is String) { + return new SpanElement()..text = ref.toString(); } throw new Exception('Unknown ref type (${ref.runtimeType})'); } diff --git a/runtime/observatory_2/lib/src/elements/instance_ref.dart b/runtime/observatory_2/lib/src/elements/instance_ref.dart index a4b3f9e33dc..e98148e86f0 100644 --- a/runtime/observatory_2/lib/src/elements/instance_ref.dart +++ b/runtime/observatory_2/lib/src/elements/instance_ref.dart @@ -127,6 +127,7 @@ class InstanceRefElement extends CustomElement implements Renderable { case M.InstanceKind.functionType: case M.InstanceKind.typeRef: case M.InstanceKind.typeParameter: + case M.InstanceKind.recordType: return [ new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) ..text = _instance.name @@ -192,12 +193,10 @@ class InstanceRefElement extends CustomElement implements Renderable { ] ]; case M.InstanceKind.mirrorReference: - return [ - new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) - ..classes = ['emphasize'] - ..text = _instance.clazz.name - ]; case M.InstanceKind.weakProperty: + case M.InstanceKind.finalizer: + case M.InstanceKind.weakReference: + case M.InstanceKind.record: return [ new AnchorElement(href: Uris.inspect(_isolate, object: _instance)) ..classes = ['emphasize'] @@ -214,6 +213,7 @@ class InstanceRefElement extends CustomElement implements Renderable { case M.InstanceKind.mirrorReference: case M.InstanceKind.stackTrace: case M.InstanceKind.weakProperty: + case M.InstanceKind.recordType: return true; case M.InstanceKind.list: case M.InstanceKind.map: @@ -337,6 +337,17 @@ class InstanceRefElement extends CustomElement implements Renderable { queue: _r.queue) .element, ]; + case M.InstanceKind.recordType: + final fields = _loadedInstance.fields.toList(); + return [ + for (int i = 0; i < fields.length; ++i) ...[ + new SpanElement()..text = '${fields[i].name} = ', + new InstanceRefElement(_isolate, fields[i].value.asValue, _objects, + queue: _r.queue) + .element, + if (i + 1 != fields.length) new BRElement(), + ] + ]; default: return []; } diff --git a/runtime/observatory_2/lib/src/elements/instance_view.dart b/runtime/observatory_2/lib/src/elements/instance_view.dart index fcc18125c78..592a978a1a1 100644 --- a/runtime/observatory_2/lib/src/elements/instance_view.dart +++ b/runtime/observatory_2/lib/src/elements/instance_view.dart @@ -334,9 +334,12 @@ class InstanceViewElement extends CustomElement implements Renderable { ..content = [ new DivElement() ..classes = ['memberList'] - ..children = fields - .map((f) => member(f.decl, f.value)) - .toList() + ..children = fields.map((f) { + final name = _instance.kind == M.InstanceKind.record + ? f.name + : f.decl; + return member(name, f.value); + }).toList() ]) .element ] diff --git a/runtime/observatory_2/lib/src/models/objects/instance.dart b/runtime/observatory_2/lib/src/models/objects/instance.dart index b21c572a942..ff914bc5840 100644 --- a/runtime/observatory_2/lib/src/models/objects/instance.dart +++ b/runtime/observatory_2/lib/src/models/objects/instance.dart @@ -126,6 +126,18 @@ enum InstanceKind { /// An instance of the Dart class RawReceivePort receivePort, + + /// An instance of Record. + record, + + /// An instance of RecordType + recordType, + + /// An instance of Finalizer + finalizer, + + /// An instance of WeakReference + weakReference, } bool isTypedData(InstanceKind kind) { @@ -454,7 +466,8 @@ abstract class Instance extends Object implements InstanceRef { abstract class BoundField { FieldRef get decl; - Guarded get value; + dynamic get name; + Guarded get value; } abstract class NativeField { diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart index 5c9f6df4967..bfe5b05d3af 100644 --- a/runtime/observatory_2/lib/src/service/object.dart +++ b/runtime/observatory_2/lib/src/service/object.dart @@ -2118,7 +2118,7 @@ class ServiceMap extends ServiceObject _map.clear(); _map.addAll(map); - name = _map['name']; + name = _map['name']?.toString(); vmName = (_map.containsKey('_vmName') ? _map['_vmName'] : name); } @@ -2800,25 +2800,33 @@ M.InstanceKind stringToInstanceKind(String s) { return M.InstanceKind.typeRef; case 'ReceivePort': return M.InstanceKind.receivePort; + case '_Record': + return M.InstanceKind.record; + case '_RecordType': + return M.InstanceKind.recordType; + case 'Finalizer': + return M.InstanceKind.finalizer; + case 'WeakReference': + return M.InstanceKind.weakReference; } var message = 'Unrecognized instance kind: $s'; Logger.root.severe(message); throw new ArgumentError(message); } -class Guarded implements M.Guarded { +class Guarded implements M.Guarded { bool get isValue => asValue != null; bool get isSentinel => asSentinel != null; final Sentinel asSentinel; final T asValue; - factory Guarded(ServiceObject obj) { + factory Guarded(dynamic obj) { if (obj is Sentinel) { return new Guarded.fromSentinel(obj); } else if (obj is T) { return new Guarded.fromValue(obj); } - throw new Exception('${obj.type} is neither Sentinel or $T'); + throw new Exception('${obj.runtimeType} is neither Sentinel or $T'); } Guarded.fromSentinel(this.asSentinel) : asValue = null; @@ -2827,8 +2835,10 @@ class Guarded implements M.Guarded { class BoundField implements M.BoundField { final Field decl; - final Guarded value; - BoundField(this.decl, value) : value = new Guarded(value); + // String|int + final dynamic name; + final Guarded value; + BoundField(this.decl, this.name, value) : value = new Guarded(value); } class NativeField implements M.NativeField { @@ -2929,7 +2939,7 @@ class Instance extends HeapObject implements M.Instance { Instance._empty(ServiceObjectOwner owner) : super._empty(owner); void _update(Map map, bool mapIsRef) { - // Extract full properties.1 + // Extract full properties. _upgradeCollection(map, isolate); super._update(map, mapIsRef); @@ -2938,7 +2948,7 @@ class Instance extends HeapObject implements M.Instance { // Coerce absence to false. valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true; closureFunction = map['closureFunction']; - name = map['name']; + name = map['name']?.toString(); length = map['length']; pattern = map['pattern']; typeClass = map['typeClass']; @@ -2971,7 +2981,7 @@ class Instance extends HeapObject implements M.Instance { if (map['fields'] != null) { var fields = []; for (var f in map['fields']) { - fields.add(new BoundField(f['decl'], f['value'])); + fields.add(new BoundField(f['decl'], f['name'], f['value'])); } this.fields = fields; } else { diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc index 053c1c7a66b..b33d27922ee 100644 --- a/runtime/vm/object_service.cc +++ b/runtime/vm/object_service.cc @@ -1128,7 +1128,7 @@ void Instance::PrintSharedInstanceJSON(JSONObject* jsobj, Array& field_array = Array::Handle(); Field& field = Field::Handle(); - Instance& field_value = Instance::Handle(); + Object& field_value = Object::Handle(); { JSONArray jsarr(jsobj, "fields"); for (intptr_t i = classes.length() - 1; i >= 0; i--) { @@ -1137,7 +1137,7 @@ void Instance::PrintSharedInstanceJSON(JSONObject* jsobj, for (intptr_t j = 0; j < field_array.Length(); j++) { field ^= field_array.At(j); if (!field.is_static()) { - field_value ^= GetField(field); + field_value = GetField(field); JSONObject jsfield(&jsarr); jsfield.AddProperty("type", "BoundField"); jsfield.AddProperty("decl", field);