mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
[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:
parent
ff058cffa6
commit
97f013773c
5 changed files with 106 additions and 9 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue