[ Service ] Fix dart:developer service extensions, add getSupportedProtocols
Brings package:vm_service test suite pass rate up to ~93% Change-Id: I108a24e1acf37eb69c9de25a2f7334eb4c56dbe6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487600 Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
@@ -6,6 +6,11 @@ export 'src/clients.dart';
|
||||
export 'src/dart_runtime_service.dart';
|
||||
export 'src/dart_runtime_service_backend.dart';
|
||||
export 'src/dart_runtime_service_options.dart';
|
||||
export 'src/dart_runtime_service_rpcs.dart'
|
||||
show
|
||||
RpcHandlerWithNoParameters,
|
||||
RpcHandlerWithParameters,
|
||||
ServiceRpcHandler;
|
||||
export 'src/event_streams.dart';
|
||||
export 'src/exceptions.dart';
|
||||
export 'src/expression_evaluator.dart';
|
||||
|
||||
@@ -76,10 +76,11 @@ base class Client {
|
||||
|
||||
@mustCallSuper
|
||||
void registerRpcHandlers() {
|
||||
_internalRpcs.registerRpcsWithPeer(_clientPeer);
|
||||
backend.registerRpcs(_clientPeer);
|
||||
_internalRpcs.registerServiceExtensionForwarder(_clientPeer);
|
||||
backend.registerFallbacks(_clientPeer);
|
||||
_internalRpcs
|
||||
..addBackendRpcs(backend: backend)
|
||||
..registerRpcsWithPeer(_clientPeer)
|
||||
..registerServiceExtensionForwarder(_clientPeer)
|
||||
..registerBackendFallbacks(_clientPeer);
|
||||
}
|
||||
|
||||
/// Attempts to register a [service] to be provided by this client.
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// 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:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'dart_runtime_service.dart';
|
||||
import 'dart_runtime_service_rpcs.dart';
|
||||
import 'event_streams.dart';
|
||||
import 'expression_evaluator.dart';
|
||||
import 'isolate_manager.dart';
|
||||
@@ -72,14 +74,14 @@ abstract class DartRuntimeServiceBackend<IM extends IsolateManager> {
|
||||
/// more clients listening to [streamId].
|
||||
void onStreamCancel({required String streamId}) {}
|
||||
|
||||
/// Invoked by the [DartRuntimeService] to register handlers for the RPCs
|
||||
/// provided by the backend.
|
||||
void registerRpcs(json_rpc.Peer clientPeer);
|
||||
/// RPCs to be registered with the [DartRuntimeService].
|
||||
UnmodifiableListView<ServiceRpcHandler> get rpcs =>
|
||||
UnmodifiableListView(const []);
|
||||
|
||||
/// Invoked by the [DartRuntimeService] to register fallback handlers
|
||||
/// provided by the backend.
|
||||
/// Fallbacks to be registered with the [DartRuntimeService].
|
||||
///
|
||||
/// Backend fallbacks are executed after incoming RPC requests fail to match
|
||||
/// any registered RPCs or service extensions provided by other clients.
|
||||
void registerFallbacks(json_rpc.Peer clientPeer);
|
||||
UnmodifiableListView<RpcHandlerWithParameters> get fallbacks =>
|
||||
UnmodifiableListView(const []);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import 'clients.dart';
|
||||
import 'dart_runtime_service.dart';
|
||||
import 'dart_runtime_service_backend.dart';
|
||||
import 'event_streams.dart';
|
||||
import 'expression_evaluator.dart';
|
||||
import 'rpc_exceptions.dart';
|
||||
@@ -20,6 +21,8 @@ typedef RpcHandlerWithNoParameters = FutureOr<RpcResponse> Function();
|
||||
typedef RpcHandlerWithParameters =
|
||||
FutureOr<RpcResponse> Function(json_rpc.Parameters);
|
||||
|
||||
typedef ServiceRpcHandler = (String, Function?);
|
||||
|
||||
/// Manages requests made to platform-agnostic RPCs provided by
|
||||
/// [DartRuntimeService] by a single [Client].
|
||||
final class DartRuntimeServiceRpcs {
|
||||
@@ -49,7 +52,7 @@ final class DartRuntimeServiceRpcs {
|
||||
// Parameters for streamListen
|
||||
static const _kStreamId = 'streamId';
|
||||
|
||||
late final _commonRpcs = <(String, Function?)>[
|
||||
late final _commonRpcs = <ServiceRpcHandler>[
|
||||
('getClientName', getClientName),
|
||||
('registerService', registerService),
|
||||
('setClientName', setClientName),
|
||||
@@ -59,9 +62,22 @@ final class DartRuntimeServiceRpcs {
|
||||
('evaluateInFrame', expressionEvaluator?.evaluateInFrame),
|
||||
];
|
||||
|
||||
/// Registers the set of platform-agnostic RPCs for use by [client].
|
||||
final _backendRpcs = <ServiceRpcHandler>[];
|
||||
final _backendFallbacks = <RpcHandlerWithParameters>[];
|
||||
|
||||
void addBackendRpcs({required DartRuntimeServiceBackend backend}) {
|
||||
_backendRpcs.addAll(backend.rpcs);
|
||||
_backendFallbacks.addAll(backend.fallbacks);
|
||||
}
|
||||
|
||||
void addBackendFallbacks({
|
||||
required List<RpcHandlerWithParameters> fallbacks,
|
||||
}) => _backendFallbacks.addAll(fallbacks);
|
||||
|
||||
/// Registers the set of platform-agnostic and backend RPCs for use by
|
||||
/// [client].
|
||||
void registerRpcsWithPeer(json_rpc.Peer clientPeer) {
|
||||
for (final (method, callback) in _commonRpcs) {
|
||||
for (final (method, callback) in [..._commonRpcs, ..._backendRpcs]) {
|
||||
if (callback == null) continue;
|
||||
if (callback is! RpcHandlerWithNoParameters &&
|
||||
callback is! RpcHandlerWithParameters) {
|
||||
@@ -92,6 +108,12 @@ final class DartRuntimeServiceRpcs {
|
||||
clientPeer.registerFallback(serviceExtensionForwarderFallback);
|
||||
}
|
||||
|
||||
void registerBackendFallbacks(json_rpc.Peer clientPeer) {
|
||||
for (final fallback in _backendFallbacks) {
|
||||
clientPeer.registerFallback(fallback);
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to [parse] [parameters] into an instance of [T].
|
||||
///
|
||||
/// If [parameters] can't be parsed, a [json_rpc.RpcException] is thrown
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// 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 'dart:collection';
|
||||
|
||||
import 'package:dart_runtime_service/dart_runtime_service.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
/// Fake implementation of [DartRuntimeServiceBackend] that throws when
|
||||
@@ -32,10 +33,12 @@ base class FakeDartRuntimeServiceBackend extends Fake
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
void registerRpcs(json_rpc.Peer clientPeer) {}
|
||||
UnmodifiableListView<ServiceRpcHandler> get rpcs =>
|
||||
UnmodifiableListView(const []);
|
||||
|
||||
@override
|
||||
void registerFallbacks(json_rpc.Peer clientPeer) {}
|
||||
UnmodifiableListView<RpcHandlerWithParameters> get fallbacks =>
|
||||
UnmodifiableListView(const []);
|
||||
|
||||
@override
|
||||
DartRuntimeService get frontend => throw UnimplementedError();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
@@ -13,6 +14,7 @@ import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
import 'src/dart_runtime_service_vm_rpcs.dart';
|
||||
import 'src/native_bindings.dart';
|
||||
import 'src/vm_expression_evaluator.dart';
|
||||
import 'src/vm_isolate_manager.dart';
|
||||
@@ -73,6 +75,20 @@ class DartRuntimeServiceVMBackend
|
||||
@override
|
||||
late final VmExpressionEvaluator expressionEvaluator;
|
||||
|
||||
final _vmServiceRpcs = DartRuntimeServiceVmRpcs();
|
||||
|
||||
@override
|
||||
UnmodifiableListView<ServiceRpcHandler> get rpcs =>
|
||||
UnmodifiableListView(_vmServiceRpcs.rpcs);
|
||||
|
||||
@override
|
||||
UnmodifiableListView<RpcHandlerWithParameters>
|
||||
get fallbacks => UnmodifiableListView([
|
||||
// If the registered Dart RPC handlers can't handle a request, forward it
|
||||
// it to the native VM service implementation for processing.
|
||||
sendToRuntime,
|
||||
]);
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {
|
||||
_logger.info('Initializing...');
|
||||
@@ -148,18 +164,6 @@ class DartRuntimeServiceVMBackend
|
||||
_nativeBindings.streamCancel(streamId: streamId);
|
||||
}
|
||||
|
||||
@override
|
||||
void registerRpcs(json_rpc.Peer clientPeer) {
|
||||
// The VM service handles its service requests in service.cc.
|
||||
}
|
||||
|
||||
@override
|
||||
void registerFallbacks(json_rpc.Peer clientPeer) {
|
||||
// If the registered Dart RPC handlers can't handle a request, forward it
|
||||
// it to the native VM service implementation for processing.
|
||||
clientPeer.registerFallback(sendToRuntime);
|
||||
}
|
||||
|
||||
/// Sends service requests to the Dart VM runtime for processing.
|
||||
Future<RpcResponse> sendToRuntime(json_rpc.Parameters request) async {
|
||||
final method = request.method;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 'dart:collection';
|
||||
|
||||
import 'package:dart_runtime_service/dart_runtime_service.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import 'native_bindings.dart';
|
||||
|
||||
/// Implementations of RPCs specific to the VM service that are not handled
|
||||
/// in runtime/vm/service.cc.
|
||||
final class DartRuntimeServiceVmRpcs {
|
||||
final _logger = Logger('$DartRuntimeServiceVmRpcs');
|
||||
final _nativeBindings = NativeBindings();
|
||||
|
||||
late final rpcs = UnmodifiableListView<ServiceRpcHandler>([
|
||||
('getSupportedProtocols', getSupportedProtocols),
|
||||
]);
|
||||
|
||||
/// Returns the list of protocols implemented by the service.
|
||||
///
|
||||
/// VM service middleware like DDS should intercept this RPC and add their
|
||||
/// own information to the response.
|
||||
Future<RpcResponse> getSupportedProtocols() async {
|
||||
final version = Version.parse(
|
||||
await _nativeBindings.sendToVM(method: 'getVersion', params: const {}),
|
||||
);
|
||||
if (version == null) {
|
||||
_logger.warning('Unable to retrieve version for getSupportedProtocols.');
|
||||
RpcException.internalError.throwException();
|
||||
}
|
||||
|
||||
return ProtocolList(
|
||||
protocols: [
|
||||
Protocol(
|
||||
protocolName: 'VM Service',
|
||||
major: version.major,
|
||||
minor: version.minor,
|
||||
),
|
||||
],
|
||||
).toJson();
|
||||
}
|
||||
}
|
||||
@@ -133,10 +133,16 @@ class NativeBindings {
|
||||
const kData = 'data';
|
||||
|
||||
final Object? converted;
|
||||
if (value case [final Uint8List utf8String]) {
|
||||
if (value case final String string) {
|
||||
converted = json.decode(string);
|
||||
} else if (value case [final Uint8List utf8String]) {
|
||||
converted = jsonUtf8Decoder.decode(utf8String);
|
||||
} else {
|
||||
RpcException.internalError.throwException();
|
||||
RpcException.internalError.throwException(
|
||||
data: {
|
||||
'details': 'Unknown response type (${value.runtimeType}: $value)',
|
||||
},
|
||||
);
|
||||
}
|
||||
if (converted case {kResult: final Map<String, Object?> result}) {
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user