[ Service ] Add support for ID zones to package:dart_runtime_service_vm
Also disables logging by default, which can be enabled by specifying the `VM_SERVICE_LOGGING` environment variable. package:vm_service test suite is ~99% passing with this change. Change-Id: I41578cc8b39afac93589b8792861be34b848f558 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/490921 Reviewed-by: Jessy Yameogo <yjessy@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
@@ -113,7 +113,7 @@ Future<void> main([List<String> args = const []]) async {
|
||||
}
|
||||
await DartRuntimeService.initialize(
|
||||
config: DartRuntimeServiceOptions(
|
||||
enableLogging: true,
|
||||
enableLogging: Platform.environment.containsKey('VM_SERVICE_LOGGING'),
|
||||
port: _port,
|
||||
disableAuthCodes: _authCodesDisabled,
|
||||
disableOriginCheck: _originCheckDisabled,
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
import 'src/dart_runtime_service_vm_rpcs.dart';
|
||||
import 'src/native_bindings.dart';
|
||||
import 'src/vm_clients.dart';
|
||||
import 'src/vm_dev_fs.dart';
|
||||
import 'src/vm_expression_evaluator.dart';
|
||||
import 'src/vm_isolate_manager.dart';
|
||||
@@ -79,7 +80,7 @@ class DartRuntimeServiceVMBackend
|
||||
@override
|
||||
late final VmExpressionEvaluator expressionEvaluator;
|
||||
|
||||
final _vmServiceRpcs = DartRuntimeServiceVmRpcs();
|
||||
late final _vmServiceRpcs = DartRuntimeServiceVmRpcs(backend: this);
|
||||
|
||||
/// Adds support for launching and accepting connections from the
|
||||
/// Dart Development Service.
|
||||
@@ -103,6 +104,10 @@ class DartRuntimeServiceVMBackend
|
||||
@override
|
||||
OptionalHandler get httpHandler => _devFs.handlePutStreamRequest;
|
||||
|
||||
@override
|
||||
VmClientManager clientManagerBuilder() =>
|
||||
VmClientManager(backend: this, eventStreamMethods: frontend.eventStreams);
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
_logger.info('Initializing...');
|
||||
|
||||
@@ -5,18 +5,34 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:dart_runtime_service/dart_runtime_service.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import '../dart_runtime_service_vm.dart';
|
||||
import 'native_bindings.dart';
|
||||
import 'vm_clients.dart';
|
||||
|
||||
/// Implementations of RPCs specific to the VM service that are not handled
|
||||
/// in runtime/vm/service.cc.
|
||||
final class DartRuntimeServiceVmRpcs {
|
||||
DartRuntimeServiceVmRpcs({required this.backend});
|
||||
|
||||
final _logger = Logger('$DartRuntimeServiceVmRpcs');
|
||||
final _nativeBindings = NativeBindings();
|
||||
final DartRuntimeServiceVMBackend backend;
|
||||
|
||||
static const _kGetSupportedProtocols = 'getSupportedProtocols';
|
||||
static const _kCreateIdZone = 'createIdZone';
|
||||
static const _kDeleteIdZone = 'deleteIdZone';
|
||||
|
||||
static const _kIsolateId = 'isolateId';
|
||||
static const _kIdZoneId = 'idZoneId';
|
||||
|
||||
late final rpcs = UnmodifiableListView<ServiceRpcHandler>([
|
||||
('getSupportedProtocols', getSupportedProtocols),
|
||||
(_kGetSupportedProtocols, getSupportedProtocols),
|
||||
(_kCreateIdZone, createIdZone),
|
||||
(_kDeleteIdZone, deleteIdZone),
|
||||
]);
|
||||
|
||||
/// Returns the list of protocols implemented by the service.
|
||||
@@ -42,4 +58,40 @@ final class DartRuntimeServiceVmRpcs {
|
||||
],
|
||||
).toJson();
|
||||
}
|
||||
|
||||
/// Creates a new [IdZone] where temporary IDs for instances in the specified
|
||||
/// isolate may be allocated for [client].
|
||||
Future<RpcResponse> createIdZone(
|
||||
json_rpc_2.Parameters parameters,
|
||||
Client client,
|
||||
) async {
|
||||
// The implementation of this RPC is in the VM, but we track which zones
|
||||
// have been created by individual clients so we can clean them up when the
|
||||
// clients disconnect.
|
||||
final result = await backend.sendToRuntime(parameters);
|
||||
final idZone = IdZone.parse(result);
|
||||
if (idZone != null) {
|
||||
final isolateId = parameters[_kIsolateId].asString;
|
||||
final vmClient = client as VmClient;
|
||||
vmClient.registerIdZone(isolateId: isolateId, idZone: idZone);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Destroys an [IdZone] owned by [client].
|
||||
Future<RpcResponse> deleteIdZone(
|
||||
json_rpc_2.Parameters parameters,
|
||||
Client client,
|
||||
) async {
|
||||
// The implementation of this RPC is in the VM, but we track which zones
|
||||
// have been created by individual clients so we can clean them up when the
|
||||
// clients disconnect.
|
||||
final result = await backend.sendToRuntime(parameters);
|
||||
final vmClient = client as VmClient;
|
||||
vmClient.unregisterIdZone(
|
||||
isolateId: parameters[_kIsolateId].asString,
|
||||
idZoneId: parameters[_kIdZoneId].asString,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
import 'package:dart_runtime_service/dart_runtime_service.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' hide Client;
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import '../dart_runtime_service_vm.dart';
|
||||
|
||||
typedef ServiceIDZone = ({IdZone idZone, String isolateId});
|
||||
|
||||
/// A [Client] of the VM service.
|
||||
final class VmClient extends Client<DartRuntimeServiceVMBackend> {
|
||||
VmClient({
|
||||
required super.connection,
|
||||
required super.clients,
|
||||
required super.eventStreamMethods,
|
||||
required super.backend,
|
||||
required super.artificial,
|
||||
super.name,
|
||||
});
|
||||
|
||||
final _idZones = <ServiceIDZone>{};
|
||||
|
||||
@override
|
||||
@protected
|
||||
Future<void> cleanup() async {
|
||||
await _cleanupIdZones();
|
||||
await super.cleanup();
|
||||
}
|
||||
|
||||
/// Track a newly created [IdZone].
|
||||
void registerIdZone({required String isolateId, required IdZone idZone}) {
|
||||
_idZones.add((idZone: idZone, isolateId: isolateId));
|
||||
}
|
||||
|
||||
/// Stop tracking a recently destroyed [IdZone].
|
||||
void unregisterIdZone({required String isolateId, required String idZoneId}) {
|
||||
_idZones.removeWhere(
|
||||
(e) => e.isolateId == isolateId && e.idZone.id == idZoneId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _cleanupIdZones() async {
|
||||
await Future.wait([
|
||||
for (final (:idZone, :isolateId) in _idZones)
|
||||
backend.sendToRuntime(
|
||||
Parameters('deleteIdZone', {
|
||||
'isolateId': isolateId,
|
||||
'idZoneId': idZone.id!,
|
||||
}),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages and tracks clients of the VM service.
|
||||
final class VmClientManager extends ClientManager<DartRuntimeServiceVMBackend> {
|
||||
VmClientManager({required super.backend, required super.eventStreamMethods});
|
||||
|
||||
@override
|
||||
VmClient clientBuilder({
|
||||
required StreamChannel<Object?> connection,
|
||||
required UnmodifiableClientNamedLookup clients,
|
||||
required EventStreamMethods eventStreamMethods,
|
||||
required DartRuntimeServiceVMBackend backend,
|
||||
required bool artificial,
|
||||
String? name,
|
||||
}) {
|
||||
return VmClient(
|
||||
connection: connection,
|
||||
clients: clients,
|
||||
eventStreamMethods: eventStreamMethods,
|
||||
backend: backend,
|
||||
name: name,
|
||||
artificial: artificial,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
static const kScope = 'scope';
|
||||
static const kDisableBreakpoints = 'disableBreakpoints';
|
||||
|
||||
// TODO(bkonyi): add ID zone support.
|
||||
// static const kIdZoneId = 'idZoneId';
|
||||
// ID zone support.
|
||||
static const kIdZoneId = 'idZoneId';
|
||||
|
||||
// `evaluate` specific parameters.
|
||||
static const kTargetId = 'targetId';
|
||||
@@ -74,6 +74,9 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
disableBreakpoints: parameters[kDisableBreakpoints].exists
|
||||
? parameters[kDisableBreakpoints].asBool
|
||||
: null,
|
||||
idZoneId: parameters[kIdZoneId].exists
|
||||
? parameters[kIdZoneId].asString
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,6 +93,9 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
disableBreakpoints: parameters[kDisableBreakpoints].exists
|
||||
? parameters[kDisableBreakpoints].asBool
|
||||
: null,
|
||||
idZoneId: parameters[kIdZoneId].exists
|
||||
? parameters[kIdZoneId].asString
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,6 +111,7 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
required String? targetId,
|
||||
required Map<String, String>? scope,
|
||||
required bool? disableBreakpoints,
|
||||
required String? idZoneId,
|
||||
}) async {
|
||||
final buildScopeResponse = await _buildScope(
|
||||
isolateId: isolateId,
|
||||
@@ -124,6 +131,7 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
targetId: targetId,
|
||||
scope: scope,
|
||||
disableBreakpoints: disableBreakpoints,
|
||||
idZoneId: idZoneId,
|
||||
kernelBase64: kernelBase64,
|
||||
);
|
||||
}
|
||||
@@ -206,6 +214,7 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
required int? frameIndex,
|
||||
required String? targetId,
|
||||
required bool? disableBreakpoints,
|
||||
required String? idZoneId,
|
||||
required String kernelBase64,
|
||||
}) {
|
||||
final params = <String, Object?>{
|
||||
@@ -215,6 +224,7 @@ final class VmExpressionEvaluator extends ExpressionEvaluator {
|
||||
kFrameIndex: ?frameIndex,
|
||||
kTargetId: ?targetId,
|
||||
kDisableBreakpoints: ?disableBreakpoints,
|
||||
kIdZoneId: ?idZoneId,
|
||||
kKernelBytes: kernelBase64,
|
||||
};
|
||||
return backend.sendToRuntime(
|
||||
|
||||
@@ -13,5 +13,6 @@ dependencies:
|
||||
file: any
|
||||
json_rpc_2: any
|
||||
logging: any
|
||||
meta: any
|
||||
stream_channel: any
|
||||
vm_service: any
|
||||
|
||||
Reference in New Issue
Block a user