Skip to content

Commit 332c804

Browse files
authored
support basic scope in evaluate calls (#344)
1 parent fab33b0 commit 332c804

File tree

5 files changed

+121
-28
lines changed

5 files changed

+121
-28
lines changed

dwds/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.2
2+
3+
- Add support for `scope` in `evaluate` calls.
4+
15
## 0.3.1
26

37
- Improve error reporting for evals, give the full JS eval in the error message

dwds/example/hello_world/main.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ void main() async {
5151
String helloString(String response) => response;
5252
bool helloBool(bool response) => response;
5353
num helloNum(num response) => response;
54+
MyTestClass createObject(String message) => MyTestClass(message: message);
55+
String messageFor(MyTestClass instance) => instance.message;
56+
String messagesCombined(MyTestClass a, MyTestClass b) => a.message + b.message;
5457

5558
class MyTestClass {
56-
String hello() => 'world';
59+
final String message;
60+
61+
MyTestClass({this.message = 'world'});
62+
63+
String hello() => message;
5764
}

dwds/lib/src/chrome_proxy_service.dart

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -246,51 +246,80 @@ require("dart_sdk").developer.invokeExtension(
246246
@override
247247
Future evaluate(String isolateId, String targetId, String expression,
248248
{Map<String, String> scope, bool disableBreakpoints}) async {
249+
scope ??= {};
250+
disableBreakpoints ??= false;
249251
var library = await _getLibrary(isolateId, targetId);
250252
if (library == null) {
251253
throw UnsupportedError(
252254
'Evaluate is only supported when `targetId` is a library.');
253255
}
254-
var evalExpression = '''
256+
WipResponse result;
257+
258+
// If there is scope, we use `callFunctionOn` because that accepts the
259+
// `arguments` option.
260+
//
261+
// If there is no scope we run a normal evaluate call.
262+
if (scope.isEmpty) {
263+
var evalExpression = '''
255264
(function() {
256265
${_getLibrarySnippet(library.uri)};
257266
return library.$expression;
258267
})();
259268
''';
260-
var result = await tabConnection.runtime.sendCommand('Runtime.evaluate',
261-
params: {'expression': evalExpression, 'returnByValue': true});
262-
_handleErrorIfPresent(result,
263-
evalContents: evalExpression,
264-
additionalDetails: {
265-
'Dart expression': expression,
266-
'scope': scope,
267-
});
269+
result = await tabConnection.runtime.sendCommand('Runtime.evaluate',
270+
params: {'expression': evalExpression});
271+
_handleErrorIfPresent(result,
272+
evalContents: evalExpression,
273+
additionalDetails: {
274+
'Dart expression': expression,
275+
});
276+
} else {
277+
var argsString = scope.keys.join(', ');
278+
var arguments = scope.values.map((id) => {'objectId': id}).toList();
279+
var evalExpression = '''
280+
function($argsString) {
281+
${_getLibrarySnippet(library.uri)};
282+
return library.$expression;
283+
}
284+
''';
285+
result = await tabConnection.runtime
286+
.sendCommand('Runtime.callFunctionOn', params: {
287+
'functionDeclaration': evalExpression,
288+
'arguments': arguments,
289+
// TODO(jakemac): Use the executionContext instead, or possibly the
290+
// library object. This will get weird if people try to use `this` in
291+
// their expression.
292+
'objectId': scope.values.first,
293+
});
294+
_handleErrorIfPresent(result,
295+
evalContents: evalExpression,
296+
additionalDetails: {
297+
'Dart expression': expression,
298+
'scope': scope,
299+
});
300+
}
268301
var remoteObject =
269302
RemoteObject(result.result['result'] as Map<String, dynamic>);
270303

271-
String kind;
272-
var classRef = ClassRef()
273-
..id = 'dart:core:${remoteObject.type}'
274-
..name = remoteObject.type;
275304
switch (remoteObject.type) {
276305
case 'string':
277-
kind = InstanceKind.kString;
278-
break;
306+
return _primitiveInstance(InstanceKind.kString, remoteObject);
279307
case 'number':
280-
kind = InstanceKind.kDouble;
281-
break;
308+
return _primitiveInstance(InstanceKind.kDouble, remoteObject);
282309
case 'boolean':
283-
kind = InstanceKind.kBool;
284-
break;
310+
return _primitiveInstance(InstanceKind.kBool, remoteObject);
311+
case 'object':
312+
return InstanceRef()
313+
..kind = InstanceKind.kPlainInstance
314+
..id = remoteObject.objectId
315+
// TODO(jakemac): Create a real ClassRef, we need a way of looking
316+
// up the library for a given instance to create it though.
317+
// https://github.com/dart-lang/sdk/issues/36771.
318+
..classRef = ClassRef();
285319
default:
286320
throw UnsupportedError(
287321
'Unsupported response type ${remoteObject.type}');
288322
}
289-
290-
return InstanceRef()
291-
..valueAsString = '${remoteObject.value}'
292-
..classRef = classRef
293-
..kind = kind;
294323
}
295324

296325
@override
@@ -765,8 +794,9 @@ require("dart_sdk").developer.invokeExtension(
765794
var inspectee = InstanceRef()
766795
..kind = InstanceKind.kPlainInstance
767796
..id = event.args[1].objectId
768-
// TODO: A real classref? we need something here so it can properly
769-
// serialize, but it isn't used by the widget inspector.
797+
// TODO(jakemac): Create a real ClassRef, we need a way of looking
798+
// up the library for a given instance to create it though.
799+
// https://github.com/dart-lang/sdk/issues/36771.
770800
..classRef = ClassRef();
771801
_streamNotify(
772802
'Debug',
@@ -870,3 +900,14 @@ const _pauseModePauseStates = {
870900
'all': PauseState.all,
871901
'unhandled': PauseState.uncaught,
872902
};
903+
904+
/// Creates an [InstanceRef] for a primitive [RemoteObject].
905+
InstanceRef _primitiveInstance(String kind, RemoteObject remoteObject) {
906+
var classRef = ClassRef()
907+
..id = 'dart:core:${remoteObject.type}'
908+
..name = kind;
909+
return InstanceRef()
910+
..valueAsString = '${remoteObject.value}'
911+
..classRef = classRef
912+
..kind = kind;
913+
}

dwds/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: dwds
2-
version: 0.3.1
2+
version: 0.3.2
33
author: Dart Team <[email protected]>
44
homepage: https://github.com/dart-lang/webdev/tree/master/dwds
55
description: >-

dwds/test/chrome_proxy_service_test.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,47 @@ void main() {
150150
const TypeMatcher<InstanceRef>().having(
151151
(instance) => instance.valueAsString, 'valueAsString', '42.2'));
152152
});
153+
154+
test('can return objects with ids', () async {
155+
expect(
156+
await service.evaluate(
157+
isolate.id, isolate.rootLib.id, 'createObject("cool")'),
158+
const TypeMatcher<InstanceRef>()
159+
.having((instance) => instance.id, 'id', isNotNull));
160+
// TODO(jakemac): Add tests for the ClassRef once we create one,
161+
// https://github.com/dart-lang/sdk/issues/36771.
162+
});
163+
164+
group('with provided scope', () {
165+
Future<InstanceRef> createRemoteObject(String message) async {
166+
return await service.evaluate(
167+
isolate.id, isolate.rootLib.id, 'createObject("$message")')
168+
as InstanceRef;
169+
}
170+
171+
test('single scope object', () async {
172+
var instance = await createRemoteObject('A');
173+
var result = await service.evaluate(
174+
isolate.id, isolate.rootLib.id, 'messageFor(arg1)',
175+
scope: {'arg1': instance.id});
176+
expect(
177+
result,
178+
const TypeMatcher<InstanceRef>().having(
179+
(instance) => instance.valueAsString, 'valueAsString', 'A'));
180+
});
181+
182+
test('multiple scope objects', () async {
183+
var instance1 = await createRemoteObject('A');
184+
var instance2 = await createRemoteObject('B');
185+
var result = await service.evaluate(
186+
isolate.id, isolate.rootLib.id, 'messagesCombined(arg1, arg2)',
187+
scope: {'arg1': instance1.id, 'arg2': instance2.id});
188+
expect(
189+
result,
190+
const TypeMatcher<InstanceRef>().having(
191+
(instance) => instance.valueAsString, 'valueAsString', 'AB'));
192+
});
193+
});
153194
});
154195
});
155196

0 commit comments

Comments
 (0)