Skip to content

Commit c54ec8d

Browse files
bkonyicommit-bot@chromium.org
authored andcommitted
[ 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 <[email protected]> Commit-Queue: Ben Konyi <[email protected]>
1 parent f0ea02b commit c54ec8d

File tree

14 files changed

+179
-9
lines changed

14 files changed

+179
-9
lines changed

pkg/dds/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
# 1.2.1
2+
3+
- Fixed issue where `evaluate` and `evaluateInFrame` were not invoking client
4+
provided implementations of `compileExpression`.
5+
16
# 1.2.0
27

3-
- Fix issue where forwarding requests with no RPC parameters would return an
8+
- Fixed issue where forwarding requests with no RPC parameters would return an
49
RPC error.
510

611
# 1.1.0

pkg/dds/lib/dds.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ part 'src/client.dart';
2929
part 'src/client_manager.dart';
3030
part 'src/constants.dart';
3131
part 'src/dds_impl.dart';
32+
part 'src/expression_evaluator.dart';
3233
part 'src/logging_repository.dart';
3334
part 'src/isolate_manager.dart';
3435
part 'src/named_lookup.dart';

pkg/dds/lib/src/client.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ class _DartDevelopmentServiceClient {
157157
return supportedProtocols;
158158
});
159159

160+
// `evaluate` and `evaluateInFrame` actually consist of multiple RPC
161+
// invocations, including a call to `compileExpression` which can be
162+
// overridden by clients which provide their own implementation (e.g.,
163+
// Flutter Tools). We handle all of this in [_ExpressionEvaluator].
164+
_clientPeer.registerMethod(
165+
'evaluate',
166+
dds.expressionEvaluator.execute,
167+
);
168+
_clientPeer.registerMethod(
169+
'evaluateInFrame',
170+
dds.expressionEvaluator.execute,
171+
);
172+
160173
// When invoked within a fallback, the next fallback will start executing.
161174
// The final fallback forwards the request to the VM service directly.
162175
@alwaysThrows

pkg/dds/lib/src/client_manager.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ class _ClientManager {
133133
}
134134
}
135135

136+
_DartDevelopmentServiceClient findFirstClientThatHandlesService(
137+
String service) {
138+
for (final client in clients) {
139+
if (client.services.containsKey(service)) {
140+
return client;
141+
}
142+
}
143+
return null;
144+
}
145+
136146
// Handles namespace generation for service extensions.
137147
static const _kServicePrologue = 's';
138148
final NamedLookup<_DartDevelopmentServiceClient> clients = NamedLookup(

pkg/dds/lib/src/dds_impl.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class _DartDevelopmentService implements DartDevelopmentService {
1111
this._authCodesEnabled,
1212
) {
1313
_clientManager = _ClientManager(this);
14+
_expressionEvaluator = _ExpressionEvaluator(this);
1415
_isolateManager = _IsolateManager(this);
1516
_loggingRepository = _LoggingRepository();
1617
_streamManager = _StreamManager(this);
@@ -197,6 +198,9 @@ class _DartDevelopmentService implements DartDevelopmentService {
197198
_ClientManager get clientManager => _clientManager;
198199
_ClientManager _clientManager;
199200

201+
_ExpressionEvaluator get expressionEvaluator => _expressionEvaluator;
202+
_ExpressionEvaluator _expressionEvaluator;
203+
200204
_IsolateManager get isolateManager => _isolateManager;
201205
_IsolateManager _isolateManager;
202206

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
part of dds;
6+
7+
/// A helper class which handles `evaluate` and `evaluateInFrame` calls by
8+
/// potentially forwarding compilation requests to an external compilation
9+
/// service like Flutter Tools.
10+
class _ExpressionEvaluator {
11+
_ExpressionEvaluator(this.dds);
12+
13+
Future<Map<String, dynamic>> execute(json_rpc.Parameters parameters) async {
14+
final isolateId = parameters['isolateId'].asString;
15+
final expression = parameters['expression'].asString;
16+
Map<String, dynamic> buildScopeResponse;
17+
18+
try {
19+
buildScopeResponse = await _buildScope(parameters);
20+
} on json_rpc.RpcException catch (e) {
21+
throw _RpcErrorCodes.buildRpcException(
22+
_RpcErrorCodes.kExpressionCompilationError,
23+
data: e.data,
24+
);
25+
}
26+
String kernelBase64;
27+
try {
28+
kernelBase64 =
29+
await _compileExpression(isolateId, expression, buildScopeResponse);
30+
} on json_rpc.RpcException catch (e) {
31+
throw _RpcErrorCodes.buildRpcException(
32+
_RpcErrorCodes.kExpressionCompilationError,
33+
data: e.data,
34+
);
35+
}
36+
return await _evaluateCompiledExpression(
37+
parameters, isolateId, kernelBase64);
38+
}
39+
40+
Future<Map<String, dynamic>> _buildScope(
41+
json_rpc.Parameters parameters) async {
42+
final params = _setupParams(parameters);
43+
params['isolateId'] = parameters['isolateId'].asString;
44+
if (parameters['scope'].asMapOr(null) != null) {
45+
params['scope'] = parameters['scope'].asMap;
46+
}
47+
return await dds._vmServiceClient.sendRequest(
48+
'_buildExpressionEvaluationScope',
49+
params,
50+
);
51+
}
52+
53+
Future<String> _compileExpression(String isolateId, String expression,
54+
Map<String, dynamic> buildScopeResponseResult) async {
55+
_DartDevelopmentServiceClient externalClient =
56+
dds.clientManager.findFirstClientThatHandlesService(
57+
'compileExpression',
58+
);
59+
60+
final compileParams = <String, dynamic>{
61+
'isolateId': isolateId,
62+
'expression': expression,
63+
'definitions': buildScopeResponseResult['param_names'],
64+
'typeDefinitions': buildScopeResponseResult['type_params_names'],
65+
'libraryUri': buildScopeResponseResult['libraryUri'],
66+
'isStatic': buildScopeResponseResult['isStatic'],
67+
};
68+
69+
final klass = buildScopeResponseResult['klass'];
70+
if (klass != null) {
71+
compileParams['klass'] = klass;
72+
}
73+
// TODO(bkonyi): handle service disappeared case?
74+
try {
75+
if (externalClient != null) {
76+
return (await externalClient.sendRequest(
77+
'compileExpression',
78+
compileParams,
79+
))['result']['kernelBytes'];
80+
} else {
81+
// Fallback to compiling using the kernel service.
82+
return (await dds._vmServiceClient.sendRequest(
83+
'_compileExpression',
84+
compileParams,
85+
))['kernelBytes'];
86+
}
87+
} on json_rpc.RpcException catch (e) {
88+
throw _RpcErrorCodes.buildRpcException(
89+
_RpcErrorCodes.kExpressionCompilationError,
90+
data: e.data,
91+
);
92+
}
93+
}
94+
95+
Future<Map<String, dynamic>> _evaluateCompiledExpression(
96+
json_rpc.Parameters parameters,
97+
String isolateId,
98+
String kernelBase64,
99+
) async {
100+
final params = _setupParams(parameters);
101+
params['isolateId'] = isolateId;
102+
params['kernelBytes'] = kernelBase64;
103+
params['disableBreakpoints'] =
104+
parameters['disableBreakpoints'].asBoolOr(false);
105+
if (parameters['scope'].asMapOr(null) != null) {
106+
params['scope'] = parameters['scope'].asMap;
107+
}
108+
return await dds._vmServiceClient.sendRequest(
109+
'_evaluateCompiledExpression',
110+
params,
111+
);
112+
}
113+
114+
Map<String, dynamic> _setupParams(json_rpc.Parameters parameters) {
115+
if (parameters.method == 'evaluateInFrame') {
116+
return <String, dynamic>{
117+
'frameIndex': parameters['frameIndex'].asInt,
118+
};
119+
} else {
120+
assert(parameters.method == 'evaluate');
121+
return <String, dynamic>{
122+
'targetId': parameters['targetId'].asString,
123+
};
124+
}
125+
}
126+
127+
final _DartDevelopmentService dds;
128+
}

pkg/dds/lib/src/rpc_error_codes.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
part of dds;
66

77
abstract class _RpcErrorCodes {
8-
static json_rpc.RpcException buildRpcException(int code) {
8+
static json_rpc.RpcException buildRpcException(int code, {dynamic data}) {
99
return json_rpc.RpcException(
1010
code,
1111
errorMessages[code],
12+
data: data,
1213
);
1314
}
1415

@@ -34,7 +35,7 @@ abstract class _RpcErrorCodes {
3435
// static const kIsolateMustHaveReloaded = 110;
3536
static const kServiceAlreadyRegistered = 111;
3637
static const kServiceDisappeared = 112;
37-
// static const kExpressionCompilationError = 113;
38+
static const kExpressionCompilationError = 113;
3839
// static const kInvalidTimelineRequest = 114;
3940

4041
// Experimental (used in private rpcs).
@@ -48,5 +49,6 @@ abstract class _RpcErrorCodes {
4849
kStreamNotSubscribed: 'Stream not subscribed',
4950
kServiceAlreadyRegistered: 'Service already registered',
5051
kServiceDisappeared: 'Service has disappeared',
52+
kExpressionCompilationError: 'Expression compilation error',
5153
};
5254
}

pkg/dds/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: >-
33
A library used to spawn the Dart Developer Service, used to communicate with
44
a Dart VM Service instance.
55
6-
version: 1.2.0
6+
version: 1.2.1
77

88
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/dds
99

runtime/observatory/tests/service/string_escaping_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ Future testStrings(Isolate isolate) async {
6565
expectTruncatedString(String varName, String varValueAsString) {
6666
Field field = lib.variables.singleWhere((v) => v.name == varName);
6767
Instance value = field.staticValue;
68-
print(value.valueAsString);
6968
expect(varValueAsString, startsWith(value.valueAsString));
7069
expect(value.valueAsStringIsTruncated, isTrue);
7170
}

runtime/vm/json_stream.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ void JSONStream::PrintError(intptr_t code, const char* details_format, ...) {
174174
va_start(args2, details_format);
175175
Utils::VSNPrint(buffer, (len + 1), details_format, args2);
176176
va_end(args2);
177-
178177
data.AddProperty("details", buffer);
179178
}
180179
}

0 commit comments

Comments
 (0)