[vm_service] Get tester example functional and clean up
Makes adjustments so `example/vm_service_tester.dart` runs successfully, and complete some clean up to make the code and comments in the file more consistent.
Closes https://github.com/dart-lang/sdk/issues/55753
TEST=N/A
Change-Id: Ib91985792a25a27008c2ef96786a40bb7a26ad57
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374242
Reviewed-by: Derek Xu <derekx@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/vm_service/example/vm_service_tester.dart b/pkg/vm_service/example/vm_service_tester.dart
index 54edcee..3311b7f 100644
--- a/pkg/vm_service/example/vm_service_tester.dart
+++ b/pkg/vm_service/example/vm_service_tester.dart
@@ -2,7 +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.
-library service_tester;
+/// An example of using the the libraries provided by `package:vm_service`.
+library;
import 'dart:async';
import 'dart:collection';
@@ -14,8 +15,8 @@
import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
-final String host = 'localhost';
-final int port = 7575;
+const String host = 'localhost';
+const int port = 7575;
late VmService serviceClient;
@@ -27,35 +28,40 @@
});
test('integration', () async {
- String sdk = path.dirname(path.dirname(Platform.resolvedExecutable));
-
- print('Using sdk at $sdk.');
+ final sdkPath = path.dirname(path.dirname(Platform.resolvedExecutable));
+ print('Using sdk at $sdkPath.');
// pause_isolates_on_start, pause_isolates_on_exit
- process = await Process.start('$sdk/bin/dart', [
- '--pause_isolates_on_start',
- '--enable-vm-service=$port',
- '--disable-service-auth-codes',
- 'example/sample_main.dart'
- ]);
+ final sampleProcess = process = await Process.start(
+ Platform.resolvedExecutable,
+ [
+ '--pause-isolates-on-start',
+ '--enable-vm-service=$port',
+ '--disable-service-auth-codes',
+ 'example/sample_main.dart',
+ ],
+ );
- print('dart process started');
+ print('Dart process started.');
- unawaited(process!.exitCode.then((code) => print('vm exited: $code')));
- process!.stdout.transform(utf8.decoder).listen(print);
- process!.stderr.transform(utf8.decoder).listen(print);
+ unawaited(sampleProcess.exitCode.then((code) => print('vm exited: $code')));
+ sampleProcess.stdout.transform(utf8.decoder).listen(print);
+ sampleProcess.stderr.transform(utf8.decoder).listen(print);
- await Future.delayed(Duration(milliseconds: 500));
+ await Future.delayed(const Duration(milliseconds: 500));
- final wsUri = 'ws://$host$port/ws';
- serviceClient = await vmServiceConnectUri(wsUri, log: StdoutLog());
+ final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws');
+ serviceClient = await vmServiceConnectUri(
+ wsUri.toString(),
+ log: StdoutLog(),
+ );
- print('socket connected');
+ print('VM service web socket connected.');
serviceClient.onSend.listen((str) => print('--> $str'));
- // The next listener will bail out if you toggle this to false, which we need
- // to do for some things like the custom service registration tests.
+ // The next listener will bail out if you toggle this to false, which is
+ // needed for some things like the custom service registration tests.
var checkResponseJsonCompatibility = true;
serviceClient.onReceive.listen((str) {
print('<-- $str');
@@ -65,25 +71,41 @@
// For each received event, check that we can deserialize it and
// reserialize it back to the same exact representation (minus private
// fields).
- var json = jsonDecode(str);
+ final json = jsonDecode(str);
var originalJson = json['result'] as Map<String, dynamic>?;
if (originalJson == null && json['method'] == 'streamNotify') {
originalJson = json['params']['event'];
}
expect(originalJson, isNotNull, reason: 'Unrecognized event type! $json');
- var instance =
- createServiceObject(originalJson, const ['Event', 'Success']);
+ final instance =
+ createServiceObject(originalJson!, const ['Event', 'Success']);
expect(instance, isNotNull,
- reason: 'failed to deserialize object $originalJson!');
+ reason: 'Failed to deserialize object $originalJson!');
- var reserializedJson = (instance as dynamic).toJson();
+ final reserializedJson = (instance as dynamic).toJson();
- forEachNestedMap(originalJson!, (obj) {
- // Private fields that we don't reproduce
+ forEachNestedMap(originalJson, (obj) {
+ // Remove private fields that we don't reproduce.
obj.removeWhere((k, v) => k.startsWith('_'));
- // Extra fields that aren't specified and we don't reproduce
+
+ // Remove extra fields that aren't specified and we don't reproduce.
obj.remove('isExport');
+ obj.remove('isolate_group');
+ obj.remove('parameterizedClass');
+
+ // Convert `Null` instances in the original JSON to
+ // just `null` as `createServiceObject` will use `null`
+ // to represent the reference.
+ obj.updateAll((key, value) {
+ if (value is Map &&
+ value['type'] == '@Instance' &&
+ value['kind'] == 'Null') {
+ return null;
+ } else {
+ return value;
+ }
+ });
});
forEachNestedMap(reserializedJson, (obj) {
@@ -104,14 +126,14 @@
unawaited(serviceClient.streamListen(EventStreams.kDebug));
unawaited(serviceClient.streamListen(EventStreams.kStdout));
- VM vm = await serviceClient.getVM();
+ final vm = await serviceClient.getVM();
print('hostCPU=${vm.hostCPU}');
print(await serviceClient.getVersion());
- List<IsolateRef> isolates = vm.isolates!;
+ final isolates = vm.isolates!;
print(isolates);
- // Disable the json reserialization checks since custom services are not
- // supported.
+ // Disable the json reserialization checks since custom services are
+ // not supported.
checkResponseJsonCompatibility = false;
await testServiceRegistration();
checkResponseJsonCompatibility = true;
@@ -119,23 +141,23 @@
await testScriptParse(vm.isolates!.first);
await testSourceReport(vm.isolates!.first);
- IsolateRef isolateRef = isolates.first;
+ final isolateRef = isolates.first;
print(await serviceClient.resume(isolateRef.id!));
- print('waiting for client to shut down...');
+ print('Waiting for service client to shut down...');
await serviceClient.dispose();
await serviceClient.onDone;
- print('service client shut down');
+ print('Service client shut down.');
});
}
-// Deeply traverses a map and calls [cb] with each nested map and the
-// parent map.
-void forEachNestedMap(Map input, Function(Map) cb) {
- var queue = Queue.from([input]);
+/// Deeply traverses the [input] map and calls [cb] with
+/// each nested map and the parent map.
+void forEachNestedMap(Map input, void Function(Map) cb) {
+ final queue = Queue.from([input]);
while (queue.isNotEmpty) {
- var next = queue.removeFirst();
+ final next = queue.removeFirst();
if (next is Map) {
cb(next);
queue.addAll(next.values);
@@ -145,7 +167,7 @@
}
}
-Future testServiceRegistration() async {
+Future<void> testServiceRegistration() async {
const String serviceName = 'serviceName';
const String serviceAlias = 'serviceAlias';
const String movedValue = 'movedValue';
@@ -157,15 +179,18 @@
};
});
await serviceClient.registerService(serviceName, serviceAlias);
- final wsUri = 'ws://$host$port/ws';
- VmService otherClient = await vmServiceConnectUri(wsUri, log: StdoutLog());
- Completer completer = Completer();
+ final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws');
+ final otherClient = await vmServiceConnectUri(
+ wsUri.toString(),
+ log: StdoutLog(),
+ );
+ final completer = Completer();
otherClient.onEvent('Service').listen((e) async {
if (e.service == serviceName && e.kind == EventKind.kServiceRegistered) {
assert(e.alias == serviceAlias);
- Response? response = await serviceClient.callMethod(
+ final response = await serviceClient.callMethod(
e.method!,
- args: <String, dynamic>{'input': movedValue},
+ args: {'input': movedValue},
);
assert(response.json!['output'] == movedValue);
completer.complete();
@@ -176,14 +201,14 @@
await otherClient.dispose();
}
-Future testScriptParse(IsolateRef isolateRef) async {
+Future<void> testScriptParse(IsolateRef isolateRef) async {
final isolateId = isolateRef.id!;
- final Isolate isolate = await serviceClient.getIsolate(isolateId);
- final Library rootLibrary =
+ final isolate = await serviceClient.getIsolate(isolateId);
+ final rootLibrary =
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
- final ScriptRef scriptRef = rootLibrary.scripts!.first;
+ final scriptRef = rootLibrary.scripts!.first;
- final Script script =
+ final script =
await serviceClient.getObject(isolateId, scriptRef.id!) as Script;
print(script);
print(script.uri);
@@ -192,21 +217,23 @@
print(script.tokenPosTable!.length);
}
-Future testSourceReport(IsolateRef isolateRef) async {
+Future<void> testSourceReport(IsolateRef isolateRef) async {
final isolateId = isolateRef.id!;
- final Isolate isolate = await serviceClient.getIsolate(isolateId);
- final Library rootLibrary =
+ final isolate = await serviceClient.getIsolate(isolateId);
+ final rootLibrary =
await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library;
- final ScriptRef scriptRef = rootLibrary.scripts!.first;
+ final scriptRef = rootLibrary.scripts!.first;
- // make sure some code has run
+ // Make sure that some code has run.
await serviceClient.resume(isolateId);
await Future.delayed(const Duration(milliseconds: 25));
- final SourceReport sourceReport = await serviceClient.getSourceReport(
- isolateId, [SourceReportKind.kCoverage],
- scriptId: scriptRef.id);
- for (SourceReportRange range in sourceReport.ranges!) {
+ final sourceReport = await serviceClient.getSourceReport(
+ isolateId,
+ [SourceReportKind.kCoverage],
+ scriptId: scriptRef.id,
+ );
+ for (final range in sourceReport.ranges!) {
print(' $range');
if (range.coverage != null) {
print(' ${range.coverage}');
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 0e4391b..6eb853f 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -2512,7 +2512,6 @@
///
/// If the field is uninitialized, the `value` will be the `NotInitialized`
/// [Sentinel].
-///
class BoundField {
static BoundField? parse(Map<String, dynamic>? json) =>
json == null ? null : BoundField._fromJson(json);
@@ -3073,10 +3072,15 @@
/// What kind of code object is this?
/*CodeKind*/ String? kind;
+ /// This code object's corresponding function.
+ @optional
+ FuncRef? function;
+
CodeRef({
this.name,
this.kind,
required String id,
+ this.function,
}) : super(
id: id,
);
@@ -3084,6 +3088,8 @@
CodeRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'] ?? '';
kind = json['kind'] ?? '';
+ function =
+ createServiceObject(json['function'], const ['FuncRef']) as FuncRef?;
}
@override
@@ -3097,6 +3103,7 @@
'name': name ?? '',
'kind': kind ?? '',
});
+ _setIfNotNull(json, 'function', function?.toJson());
return json;
}
@@ -3123,10 +3130,16 @@
@override
/*CodeKind*/ String? kind;
+ /// This code object's corresponding function.
+ @optional
+ @override
+ FuncRef? function;
+
Code({
this.name,
this.kind,
required String id,
+ this.function,
}) : super(
id: id,
);
@@ -3134,6 +3147,8 @@
Code._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'] ?? '';
kind = json['kind'] ?? '';
+ function =
+ createServiceObject(json['function'], const ['FuncRef']) as FuncRef?;
}
@override
@@ -3147,6 +3162,7 @@
'name': name ?? '',
'kind': kind ?? '',
});
+ _setIfNotNull(json, 'function', function?.toJson());
return json;
}
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index 8c9e858..5c714ec 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -2085,6 +2085,9 @@
// What kind of code object is this?
CodeKind kind;
+
+ // This code object's corresponding function.
+ @Function function [optional];
}
```
@@ -2097,6 +2100,9 @@
// What kind of code object is this?
CodeKind kind;
+
+ // This code object's corresponding function.
+ @Function function [optional];
}
```
@@ -3188,7 +3194,7 @@
// RegExp
@Instance pattern [optional];
-// The function associated with a Closure instance.
+ // The function associated with a Closure instance.
//
// Provided for instance kinds:
// Closure