mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
[ package:dds ] Handle expression evaluation invocations in DDS and forward compileExpression
calls to clients which provide their own compilation service.
Change-Id: I2daec26929ad4f530d28d0073a0b2758850bec0a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150694 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
parent
f0ea02bc51
commit
c54ec8d5f3
|
@ -1,6 +1,11 @@
|
|||
# 1.2.1
|
||||
|
||||
- Fixed issue where `evaluate` and `evaluateInFrame` were not invoking client
|
||||
provided implementations of `compileExpression`.
|
||||
|
||||
# 1.2.0
|
||||
|
||||
- Fix issue where forwarding requests with no RPC parameters would return an
|
||||
- Fixed issue where forwarding requests with no RPC parameters would return an
|
||||
RPC error.
|
||||
|
||||
# 1.1.0
|
||||
|
|
|
@ -29,6 +29,7 @@ part 'src/client.dart';
|
|||
part 'src/client_manager.dart';
|
||||
part 'src/constants.dart';
|
||||
part 'src/dds_impl.dart';
|
||||
part 'src/expression_evaluator.dart';
|
||||
part 'src/logging_repository.dart';
|
||||
part 'src/isolate_manager.dart';
|
||||
part 'src/named_lookup.dart';
|
||||
|
|
|
@ -157,6 +157,19 @@ class _DartDevelopmentServiceClient {
|
|||
return supportedProtocols;
|
||||
});
|
||||
|
||||
// `evaluate` and `evaluateInFrame` actually consist of multiple RPC
|
||||
// invocations, including a call to `compileExpression` which can be
|
||||
// overridden by clients which provide their own implementation (e.g.,
|
||||
// Flutter Tools). We handle all of this in [_ExpressionEvaluator].
|
||||
_clientPeer.registerMethod(
|
||||
'evaluate',
|
||||
dds.expressionEvaluator.execute,
|
||||
);
|
||||
_clientPeer.registerMethod(
|
||||
'evaluateInFrame',
|
||||
dds.expressionEvaluator.execute,
|
||||
);
|
||||
|
||||
// When invoked within a fallback, the next fallback will start executing.
|
||||
// The final fallback forwards the request to the VM service directly.
|
||||
@alwaysThrows
|
||||
|
|
|
@ -133,6 +133,16 @@ class _ClientManager {
|
|||
}
|
||||
}
|
||||
|
||||
_DartDevelopmentServiceClient findFirstClientThatHandlesService(
|
||||
String service) {
|
||||
for (final client in clients) {
|
||||
if (client.services.containsKey(service)) {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handles namespace generation for service extensions.
|
||||
static const _kServicePrologue = 's';
|
||||
final NamedLookup<_DartDevelopmentServiceClient> clients = NamedLookup(
|
||||
|
|
|
@ -11,6 +11,7 @@ class _DartDevelopmentService implements DartDevelopmentService {
|
|||
this._authCodesEnabled,
|
||||
) {
|
||||
_clientManager = _ClientManager(this);
|
||||
_expressionEvaluator = _ExpressionEvaluator(this);
|
||||
_isolateManager = _IsolateManager(this);
|
||||
_loggingRepository = _LoggingRepository();
|
||||
_streamManager = _StreamManager(this);
|
||||
|
@ -197,6 +198,9 @@ class _DartDevelopmentService implements DartDevelopmentService {
|
|||
_ClientManager get clientManager => _clientManager;
|
||||
_ClientManager _clientManager;
|
||||
|
||||
_ExpressionEvaluator get expressionEvaluator => _expressionEvaluator;
|
||||
_ExpressionEvaluator _expressionEvaluator;
|
||||
|
||||
_IsolateManager get isolateManager => _isolateManager;
|
||||
_IsolateManager _isolateManager;
|
||||
|
||||
|
|
128
pkg/dds/lib/src/expression_evaluator.dart
Normal file
128
pkg/dds/lib/src/expression_evaluator.dart
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
part of dds;
|
||||
|
||||
/// A helper class which handles `evaluate` and `evaluateInFrame` calls by
|
||||
/// potentially forwarding compilation requests to an external compilation
|
||||
/// service like Flutter Tools.
|
||||
class _ExpressionEvaluator {
|
||||
_ExpressionEvaluator(this.dds);
|
||||
|
||||
Future<Map<String, dynamic>> execute(json_rpc.Parameters parameters) async {
|
||||
final isolateId = parameters['isolateId'].asString;
|
||||
final expression = parameters['expression'].asString;
|
||||
Map<String, dynamic> buildScopeResponse;
|
||||
|
||||
try {
|
||||
buildScopeResponse = await _buildScope(parameters);
|
||||
} on json_rpc.RpcException catch (e) {
|
||||
throw _RpcErrorCodes.buildRpcException(
|
||||
_RpcErrorCodes.kExpressionCompilationError,
|
||||
data: e.data,
|
||||
);
|
||||
}
|
||||
String kernelBase64;
|
||||
try {
|
||||
kernelBase64 =
|
||||
await _compileExpression(isolateId, expression, buildScopeResponse);
|
||||
} on json_rpc.RpcException catch (e) {
|
||||
throw _RpcErrorCodes.buildRpcException(
|
||||
_RpcErrorCodes.kExpressionCompilationError,
|
||||
data: e.data,
|
||||
);
|
||||
}
|
||||
return await _evaluateCompiledExpression(
|
||||
parameters, isolateId, kernelBase64);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _buildScope(
|
||||
json_rpc.Parameters parameters) async {
|
||||
final params = _setupParams(parameters);
|
||||
params['isolateId'] = parameters['isolateId'].asString;
|
||||
if (parameters['scope'].asMapOr(null) != null) {
|
||||
params['scope'] = parameters['scope'].asMap;
|
||||
}
|
||||
return await dds._vmServiceClient.sendRequest(
|
||||
'_buildExpressionEvaluationScope',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _compileExpression(String isolateId, String expression,
|
||||
Map<String, dynamic> buildScopeResponseResult) async {
|
||||
_DartDevelopmentServiceClient externalClient =
|
||||
dds.clientManager.findFirstClientThatHandlesService(
|
||||
'compileExpression',
|
||||
);
|
||||
|
||||
final compileParams = <String, dynamic>{
|
||||
'isolateId': isolateId,
|
||||
'expression': expression,
|
||||
'definitions': buildScopeResponseResult['param_names'],
|
||||
'typeDefinitions': buildScopeResponseResult['type_params_names'],
|
||||
'libraryUri': buildScopeResponseResult['libraryUri'],
|
||||
'isStatic': buildScopeResponseResult['isStatic'],
|
||||
};
|
||||
|
||||
final klass = buildScopeResponseResult['klass'];
|
||||
if (klass != null) {
|
||||
compileParams['klass'] = klass;
|
||||
}
|
||||
// TODO(bkonyi): handle service disappeared case?
|
||||
try {
|
||||
if (externalClient != null) {
|
||||
return (await externalClient.sendRequest(
|
||||
'compileExpression',
|
||||
compileParams,
|
||||
))['result']['kernelBytes'];
|
||||
} else {
|
||||
// Fallback to compiling using the kernel service.
|
||||
return (await dds._vmServiceClient.sendRequest(
|
||||
'_compileExpression',
|
||||
compileParams,
|
||||
))['kernelBytes'];
|
||||
}
|
||||
} on json_rpc.RpcException catch (e) {
|
||||
throw _RpcErrorCodes.buildRpcException(
|
||||
_RpcErrorCodes.kExpressionCompilationError,
|
||||
data: e.data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _evaluateCompiledExpression(
|
||||
json_rpc.Parameters parameters,
|
||||
String isolateId,
|
||||
String kernelBase64,
|
||||
) async {
|
||||
final params = _setupParams(parameters);
|
||||
params['isolateId'] = isolateId;
|
||||
params['kernelBytes'] = kernelBase64;
|
||||
params['disableBreakpoints'] =
|
||||
parameters['disableBreakpoints'].asBoolOr(false);
|
||||
if (parameters['scope'].asMapOr(null) != null) {
|
||||
params['scope'] = parameters['scope'].asMap;
|
||||
}
|
||||
return await dds._vmServiceClient.sendRequest(
|
||||
'_evaluateCompiledExpression',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _setupParams(json_rpc.Parameters parameters) {
|
||||
if (parameters.method == 'evaluateInFrame') {
|
||||
return <String, dynamic>{
|
||||
'frameIndex': parameters['frameIndex'].asInt,
|
||||
};
|
||||
} else {
|
||||
assert(parameters.method == 'evaluate');
|
||||
return <String, dynamic>{
|
||||
'targetId': parameters['targetId'].asString,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final _DartDevelopmentService dds;
|
||||
}
|
|
@ -5,10 +5,11 @@
|
|||
part of dds;
|
||||
|
||||
abstract class _RpcErrorCodes {
|
||||
static json_rpc.RpcException buildRpcException(int code) {
|
||||
static json_rpc.RpcException buildRpcException(int code, {dynamic data}) {
|
||||
return json_rpc.RpcException(
|
||||
code,
|
||||
errorMessages[code],
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -34,7 +35,7 @@ abstract class _RpcErrorCodes {
|
|||
// static const kIsolateMustHaveReloaded = 110;
|
||||
static const kServiceAlreadyRegistered = 111;
|
||||
static const kServiceDisappeared = 112;
|
||||
// static const kExpressionCompilationError = 113;
|
||||
static const kExpressionCompilationError = 113;
|
||||
// static const kInvalidTimelineRequest = 114;
|
||||
|
||||
// Experimental (used in private rpcs).
|
||||
|
@ -48,5 +49,6 @@ abstract class _RpcErrorCodes {
|
|||
kStreamNotSubscribed: 'Stream not subscribed',
|
||||
kServiceAlreadyRegistered: 'Service already registered',
|
||||
kServiceDisappeared: 'Service has disappeared',
|
||||
kExpressionCompilationError: 'Expression compilation error',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ description: >-
|
|||
A library used to spawn the Dart Developer Service, used to communicate with
|
||||
a Dart VM Service instance.
|
||||
|
||||
version: 1.2.0
|
||||
version: 1.2.1
|
||||
|
||||
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ Future testStrings(Isolate isolate) async {
|
|||
expectTruncatedString(String varName, String varValueAsString) {
|
||||
Field field = lib.variables.singleWhere((v) => v.name == varName);
|
||||
Instance value = field.staticValue;
|
||||
print(value.valueAsString);
|
||||
expect(varValueAsString, startsWith(value.valueAsString));
|
||||
expect(value.valueAsStringIsTruncated, isTrue);
|
||||
}
|
||||
|
|
|
@ -174,7 +174,6 @@ void JSONStream::PrintError(intptr_t code, const char* details_format, ...) {
|
|||
va_start(args2, details_format);
|
||||
Utils::VSNPrint(buffer, (len + 1), details_format, args2);
|
||||
va_end(args2);
|
||||
|
||||
data.AddProperty("details", buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ class Zone;
|
|||
//
|
||||
// - runtime/vm/service/vmservice.dart
|
||||
// - runtime/observatory/lib/src/service/object.dart
|
||||
// - pkg/dds/lib/src/rpc_error_codes.dart
|
||||
//
|
||||
enum JSONRpcErrorCode {
|
||||
kParseError = -32700,
|
||||
|
|
|
@ -362,7 +362,7 @@ class Server {
|
|||
});
|
||||
} else {
|
||||
// Forward the websocket connection request to DDS.
|
||||
request.response.redirect(_service.ddsUri);
|
||||
request.response.redirect(_service.ddsUri!);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,14 @@ class _Evaluator {
|
|||
_Evaluator(this._message, this._isolate, this._service);
|
||||
|
||||
Future<Response> run() async {
|
||||
if (_service.ddsUri != null) {
|
||||
return Response.from(encodeRpcError(
|
||||
_message,
|
||||
kInternalError,
|
||||
details: 'Fell through to VM Service expression evaluation when a DDS '
|
||||
'instance was connected. Please file an issue on GitHub.',
|
||||
));
|
||||
}
|
||||
final buildScopeResponse = await _buildScope();
|
||||
final responseJson = buildScopeResponse.decodeJson();
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ final serviceAuthToken = _makeAuthToken();
|
|||
final isolateEmbedderData = <int, IsolateEmbedderData>{};
|
||||
|
||||
// These must be kept in sync with the declarations in vm/json_stream.h and
|
||||
// pkg/dds/lib/src/stream_manager.dart.
|
||||
// pkg/dds/lib/src/rpc_error_codes.dart.
|
||||
const kParseError = -32700;
|
||||
const kInvalidRequest = -32600;
|
||||
const kMethodNotFound = -32601;
|
||||
|
@ -219,7 +219,7 @@ class VMService extends MessageRouter {
|
|||
|
||||
final devfs = DevFS();
|
||||
|
||||
Uri get ddsUri => _ddsUri!;
|
||||
Uri? get ddsUri => _ddsUri;
|
||||
Uri? _ddsUri;
|
||||
|
||||
Future<String> _yieldControlToDDS(Message message) async {
|
||||
|
|
Loading…
Reference in a new issue