[dds] Support custom requests in DAP and verify DDS is available

Change-Id: I41be607668feaab30ae10b4fe725ad3f89e4a8ba
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203861
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Danny Tuppeny 2021-06-21 20:40:20 +00:00 committed by commit-bot@chromium.org
parent ff058cffa6
commit 97f013773c
5 changed files with 106 additions and 9 deletions

View file

@ -12,6 +12,7 @@ import '../base_debug_adapter.dart';
import '../exceptions.dart';
import '../isolate_manager.dart';
import '../logging.dart';
import '../protocol_common.dart';
import '../protocol_converter.dart';
import '../protocol_generated.dart';
import '../protocol_stream.dart';
@ -253,6 +254,39 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
sendResponse(ContinueResponseBody(allThreadsContinued: false));
}
/// [customRequest] handles any messages that do not match standard messages
/// in the spec.
///
/// This is used to allow a client/DA to have custom methods outside of the
/// spec. It is up to the client/DA to negotiate which custom messages are
/// allowed.
///
/// Implementations of this method must call super for any requests they are
/// not handling. The base implementation will reject the request as unknown.
///
/// Custom message starting with _ are considered internal and are liable to
/// change without warning.
@override
Future<void> customRequest(
Request request,
RawRequestArguments? args,
void Function(Object?) sendResponse,
) async {
switch (request.command) {
/// Used by tests to validate available protocols (eg. DDS). There may be
/// value in making this available to clients in future, but for now it's
/// internal.
case '_getSupportedProtocols':
final protocols = await vmService?.getSupportedProtocols();
sendResponse(protocols?.toJson());
break;
default:
await super.customRequest(request, args, sendResponse);
}
}
/// Overridden by sub-classes to perform any additional setup after the VM
/// Service is connected.
Future<void> debuggerConnected(vm.VM vmInfo);

View file

@ -4,6 +4,8 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'exceptions.dart';
import 'logging.dart';
import 'protocol_common.dart';
@ -59,6 +61,22 @@ abstract class BaseDebugAdapter<TLaunchArgs extends LaunchRequestArguments> {
void Function(ContinueResponseBody) sendResponse,
);
@mustCallSuper
Future<void> customRequest(
Request request,
RawRequestArguments? args,
void Function(Object?) sendResponse,
) async {
final response = Response(
success: false,
requestSeq: request.seq,
seq: _sequence++,
command: request.command,
message: 'Unknown command: ${request.command}',
);
sendResponse(response);
}
Future<void> disconnectRequest(
Request request,
DisconnectArguments? args,
@ -278,8 +296,11 @@ abstract class BaseDebugAdapter<TLaunchArgs extends LaunchRequestArguments> {
StepInArguments.fromJson,
);
} else if (request.command == 'stepOut') {
handle(request, _withVoidResponse(stepOutRequest),
StepOutArguments.fromJson);
handle(
request,
_withVoidResponse(stepOutRequest),
StepOutArguments.fromJson,
);
} else if (request.command == 'stackTrace') {
handle(request, stackTraceRequest, StackTraceArguments.fromJson);
} else if (request.command == 'scopes') {
@ -289,14 +310,11 @@ abstract class BaseDebugAdapter<TLaunchArgs extends LaunchRequestArguments> {
} else if (request.command == 'evaluate') {
handle(request, evaluateRequest, EvaluateArguments.fromJson);
} else {
final response = Response(
success: false,
requestSeq: request.seq,
seq: _sequence++,
command: request.command,
message: 'Unknown command: ${request.command}',
handle(
request,
customRequest,
_allowNullArg(RawRequestArguments.fromJson),
);
_channel.sendResponse(response);
}
}

View file

@ -8,6 +8,19 @@ abstract class EventBody {
static bool canParse(Object? obj) => obj is Map<String, Object?>?;
}
/// A generic arguments class that just supplies the arguments map directly.
///
/// Used to support custom requests that may be provided by other implementing
/// adapters that are not known at compile time by DDS/base DAP.
class RawRequestArguments extends RequestArguments {
final Map<String, Object?> args;
RawRequestArguments.fromMap(this.args);
static RawRequestArguments fromJson(Map<String, Object?> obj) =>
RawRequestArguments.fromMap(obj);
}
/// A base class for (spec-generated) classes that represent the `arguments` of
/// a request.
abstract class RequestArguments {

View file

@ -2,8 +2,11 @@
// 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:collection/collection.dart';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'test_client.dart';
import 'test_support.dart';
main() {
@ -42,6 +45,30 @@ void main(List<String> args) async {
'Exited.',
]);
});
test('connects with DDS', () async {
final client = dap.client;
final testFile = dap.createTestFile(r'''
void main(List<String> args) async {
print('Hello!'); // BREAKPOINT
}
''');
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
await client.hitBreakpoint(testFile, breakpointLine);
final response = await client.custom(
'_getSupportedProtocols',
null,
);
// For convenience, use the ProtocolList to deserialise the custom
// response to check if included DDS.
final protocolList =
ProtocolList.parse(response.body as Map<String, Object?>?);
final ddsProtocol = protocolList?.protocols
?.singleWhereOrNull((protocol) => protocol.protocolName == "DDS");
expect(ddsProtocol, isNot(isNull));
});
// These tests can be slow due to starting up the external server process.
}, timeout: Timeout.none);
});

View file

@ -77,6 +77,11 @@ class DapTestClient {
Future<Response> continue_(int threadId) =>
sendRequest(ContinueArguments(threadId: threadId));
/// Sends a custom request to the server and waits for a response.
Future<Response> custom(String name, Object? args) async {
return sendRequest(args, overrideCommand: name);
}
Future<Response> disconnect() => sendRequest(DisconnectArguments());
/// Sends an evaluate request for the given [expression], optionally for a