OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:collection'; | 6 import 'dart:collection'; |
7 | 7 |
8 import 'package:async/async.dart'; | 8 import 'package:async/async.dart'; |
9 import 'package:crypto/crypto.dart'; | 9 import 'package:crypto/crypto.dart'; |
10 import 'package:json_rpc_2/json_rpc_2.dart' as rpc; | 10 import 'package:json_rpc_2/json_rpc_2.dart' as rpc; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
61 /// A broadcast stream that emits a [VMPauseEvent] whenever this isolate is | 61 /// A broadcast stream that emits a [VMPauseEvent] whenever this isolate is |
62 /// paused or resumed. | 62 /// paused or resumed. |
63 Stream<VMPauseEvent> get onPauseOrResume => _onPauseOrResume; | 63 Stream<VMPauseEvent> get onPauseOrResume => _onPauseOrResume; |
64 Stream<VMPauseEvent> _onPauseOrResume; | 64 Stream<VMPauseEvent> _onPauseOrResume; |
65 | 65 |
66 /// A broadcast stream that emits a [VMBreakpoint] whenever a breakpoint is | 66 /// A broadcast stream that emits a [VMBreakpoint] whenever a breakpoint is |
67 /// added. | 67 /// added. |
68 Stream<VMBreakpoint> get onBreakpointAdded => _onBreakpointAdded; | 68 Stream<VMBreakpoint> get onBreakpointAdded => _onBreakpointAdded; |
69 Stream<VMBreakpoint> _onBreakpointAdded; | 69 Stream<VMBreakpoint> _onBreakpointAdded; |
70 | 70 |
71 /// A broadcast stream that emits custom events posted via `postEvent` from | |
72 /// the `dart:developer` package. | |
73 Stream<VMExtensionEvent> get onExtensionEvent => _onExtensionEvent; | |
74 Stream<VMExtensionEvent> _onExtensionEvent; | |
75 | |
76 /// A broadcast stream that emits custom events posted via `postEvent` from | |
77 /// the `dart:developer` package, limited to specific [kind]. | |
78 /// | |
79 /// If [prefix] is `true`, then [kind] is matched to the event kind as a | |
80 /// prefix. | |
81 Stream<VMExtensionEvent> selectExtensionEvents(String kind, | |
82 {bool prefix: false}) => | |
83 transform(_onExtensionEvent, new _VMExtensionEventSelector(kind, prefix)); | |
nweiz
2016/02/17 21:35:41
I'd rather this just be an anonymous function.
yjbanov
2016/02/17 23:51:43
Done.
| |
84 | |
71 /// A broadcast stream that emits this isolate's standard output. | 85 /// A broadcast stream that emits this isolate's standard output. |
72 /// | 86 /// |
73 /// This is only usable for embedders that provide access to `dart:io`. | 87 /// This is only usable for embedders that provide access to `dart:io`. |
74 /// | 88 /// |
75 /// Note that as of the VM service version 3.0, this stream doesn't emit | 89 /// Note that as of the VM service version 3.0, this stream doesn't emit |
76 /// strings passed to the `print()` function unless the host process's | 90 /// strings passed to the `print()` function unless the host process's |
77 /// standard output is actively being drained (see [sdk#24351][]). | 91 /// standard output is actively being drained (see [sdk#24351][]). |
78 /// | 92 /// |
79 /// [sdk#24351]: https://github.com/dart-lang/sdk/issues/24351 | 93 /// [sdk#24351]: https://github.com/dart-lang/sdk/issues/24351 |
80 Stream<List<int>> get stdout => _stdout; | 94 Stream<List<int>> get stdout => _stdout; |
81 Stream<List<int>> _stdout; | 95 Stream<List<int>> _stdout; |
82 | 96 |
83 /// A broadcast stream that emits this isolate's standard error. | 97 /// A broadcast stream that emits this isolate's standard error. |
84 /// | 98 /// |
85 /// This is only usable for embedders that provide access to `dart:io`. | 99 /// This is only usable for embedders that provide access to `dart:io`. |
86 Stream<List<int>> get stderr => _stderr; | 100 Stream<List<int>> get stderr => _stderr; |
87 Stream<List<int>> _stderr; | 101 Stream<List<int>> _stderr; |
88 | 102 |
103 /// A broadcast stream that emits the name of VM service extensions | |
104 Stream<String> get onServiceExtensionAdded => | |
105 _onServiceExtensionAdded; | |
106 Stream<String> _onServiceExtensionAdded; | |
107 | |
89 /// A future that fires when the isolate exits. | 108 /// A future that fires when the isolate exits. |
90 /// | 109 /// |
91 /// If the isolate has already exited, this will complete immediately. | 110 /// If the isolate has already exited, this will complete immediately. |
92 Future get onExit => _onExitMemo.runOnce(() async { | 111 Future get onExit => _onExitMemo.runOnce(() async { |
93 try { | 112 try { |
94 await _scope.getInState(_scope.streams.isolate, () async { | 113 await _scope.getInState(_scope.streams.isolate, () async { |
95 try { | 114 try { |
96 await load(); | 115 await load(); |
97 return null; | 116 return null; |
98 } on VMSentinelException catch (_) { | 117 } on VMSentinelException catch (_) { |
(...skipping 20 matching lines...) Expand all Loading... | |
119 name = json["name"] { | 138 name = json["name"] { |
120 _onGC = _transform(_scope.streams.gc, (json, sink) { | 139 _onGC = _transform(_scope.streams.gc, (json, sink) { |
121 if (json["kind"] == "GC") sink.add(null); | 140 if (json["kind"] == "GC") sink.add(null); |
122 }); | 141 }); |
123 | 142 |
124 _onUpdate = _transform(_scope.streams.isolate, (json, sink) { | 143 _onUpdate = _transform(_scope.streams.isolate, (json, sink) { |
125 if (json["kind"] != "IsolateUpdate") return; | 144 if (json["kind"] != "IsolateUpdate") return; |
126 sink.add(new VMIsolateRef._(_scope, json["isolate"])); | 145 sink.add(new VMIsolateRef._(_scope, json["isolate"])); |
127 }); | 146 }); |
128 | 147 |
148 _onServiceExtensionAdded = _transform(_scope.streams.isolate, (json, sink) { | |
149 if (json["kind"] != "ServiceExtensionAdded") return; | |
150 sink.add(json["extensionRPC"]); | |
151 }); | |
152 | |
129 _onPauseOrResume = _transform(_scope.streams.debug, (json, sink) { | 153 _onPauseOrResume = _transform(_scope.streams.debug, (json, sink) { |
130 var event = newVMPauseEvent(_scope, json); | 154 var event = newVMPauseEvent(_scope, json); |
131 if (event != null) sink.add(event); | 155 if (event != null) sink.add(event); |
132 }); | 156 }); |
133 | 157 |
134 _onBreakpointAdded = _transform(_scope.streams.debug, (json, sink) { | 158 _onBreakpointAdded = _transform(_scope.streams.debug, (json, sink) { |
135 if (json["kind"] != "BreakpointAdded") return; | 159 if (json["kind"] != "BreakpointAdded") return; |
136 sink.add(newVMBreakpoint(_scope, json["breakpoint"])); | 160 sink.add(newVMBreakpoint(_scope, json["breakpoint"])); |
137 }); | 161 }); |
138 | 162 |
139 _stdout = _transform(_scope.streams.stdout, (json, sink) { | 163 _stdout = _transform(_scope.streams.stdout, (json, sink) { |
140 if (json["kind"] != "WriteEvent") return; | 164 if (json["kind"] != "WriteEvent") return; |
141 var bytes = CryptoUtils.base64StringToBytes(json["bytes"]); | 165 var bytes = CryptoUtils.base64StringToBytes(json["bytes"]); |
142 sink.add(bytes); | 166 sink.add(bytes); |
143 }); | 167 }); |
144 | 168 |
145 _stderr = _transform(_scope.streams.stderr, (json, sink) { | 169 _stderr = _transform(_scope.streams.stderr, (json, sink) { |
146 if (json["kind"] != "WriteEvent") return; | 170 if (json["kind"] != "WriteEvent") return; |
147 sink.add(CryptoUtils.base64StringToBytes(json["bytes"])); | 171 sink.add(CryptoUtils.base64StringToBytes(json["bytes"])); |
148 }); | 172 }); |
173 | |
174 _onExtensionEvent = _transform(_scope.streams.extension, (json, sink) { | |
175 sink.add( | |
176 new VMExtensionEvent._(json['extensionKind'], json['extensionData'])); | |
177 }); | |
149 } | 178 } |
150 | 179 |
151 /// Like [transform], but only calls [handleData] for events related to this | 180 /// Like [transform], but only calls [handleData] for events related to this |
152 /// isolate. | 181 /// isolate. |
153 Stream _transform(Stream<Map> stream, handleData(Map json, StreamSink sink)) { | 182 Stream _transform(Stream<Map> stream, handleData(Map json, StreamSink sink)) { |
154 return transform(stream, (json, sink) { | 183 return transform(stream, (json, sink) { |
155 if (json["isolate"]["id"] != _scope.isolateId) return; | 184 if (json["isolate"]["id"] != _scope.isolateId) return; |
156 handleData(json, sink); | 185 handleData(json, sink); |
157 }); | 186 }); |
158 } | 187 } |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
257 var response = await _scope.sendRequest( | 286 var response = await _scope.sendRequest( |
258 "addBreakpointWithScriptUri", params); | 287 "addBreakpointWithScriptUri", params); |
259 return newVMBreakpoint(_scope, response); | 288 return newVMBreakpoint(_scope, response); |
260 } on rpc.RpcException catch (error) { | 289 } on rpc.RpcException catch (error) { |
261 // Error 102 indicates that the breakpoint couldn't be created. | 290 // Error 102 indicates that the breakpoint couldn't be created. |
262 if (error.code == 102) return null; | 291 if (error.code == 102) return null; |
263 rethrow; | 292 rethrow; |
264 } | 293 } |
265 } | 294 } |
266 | 295 |
296 /// Returns a future that completes once extension with the given [name] is | |
297 /// available. | |
298 /// | |
299 /// This works whether the extension is already registered or has yet to be | |
300 /// registered. | |
301 Future waitForExtension(String name) async { | |
302 return _scope.getInState(_scope.streams.isolate, () async { | |
303 var extensions = (await load()).extensionRPCs; | |
304 return extensions.contains(name); | |
305 }, (Map json) { | |
306 return json["kind"] == "ServiceExtensionAdded" && | |
307 json["extensionRPC"] == name; | |
308 }).then((_) => null); | |
309 } | |
310 | |
311 /// Makes a raw RPC to a VM service extension registered in this isolate | |
312 /// corresponding to the ID [number]. | |
313 /// | |
314 /// [method] must correspond to a VM service extension installed on the VM | |
315 /// isolate and it must begin with prefix "ext.". | |
316 /// | |
317 /// [params] are passed to the extension handler and must be serializable to | |
318 /// a JSON string. | |
319 Future<Object> invokeExtension(String method, [Map<String, String> params]) { | |
320 if (!method.startsWith('ext.')) { | |
321 throw new ArgumentError.value(method, 'method', | |
322 'must begin with "ext." prefix'); | |
323 } | |
324 return _scope.sendRequest(method, params); | |
325 } | |
326 | |
267 bool operator ==(other) => other is VMIsolateRef && | 327 bool operator ==(other) => other is VMIsolateRef && |
268 other._scope.isolateId == _scope.isolateId; | 328 other._scope.isolateId == _scope.isolateId; |
269 | 329 |
270 int get hashCode => _scope.isolateId.hashCode; | 330 int get hashCode => _scope.isolateId.hashCode; |
271 | 331 |
272 String toString() => name; | 332 String toString() => name; |
273 } | 333 } |
274 | 334 |
275 /// A full isolate on the remote VM. | 335 /// A full isolate on the remote VM. |
276 class VMIsolate extends VMIsolateRef { | 336 class VMIsolate extends VMIsolateRef { |
(...skipping 13 matching lines...) Expand all Loading... | |
290 | 350 |
291 /// Whether this isolate is paused. | 351 /// Whether this isolate is paused. |
292 bool get isPaused => pauseEvent is! VMResumeEvent; | 352 bool get isPaused => pauseEvent is! VMResumeEvent; |
293 | 353 |
294 /// The error that's causing the isolate to exit or `null`. | 354 /// The error that's causing the isolate to exit or `null`. |
295 final VMError error; | 355 final VMError error; |
296 | 356 |
297 /// All breakpoints currently registered for this isolate. | 357 /// All breakpoints currently registered for this isolate. |
298 final List<VMBreakpoint> breakpoints; | 358 final List<VMBreakpoint> breakpoints; |
299 | 359 |
360 /// The list of service extension RPCs that are registered for this isolate. | |
361 final List<String> extensionRPCs; | |
nweiz
2016/02/17 21:35:41
Nit: Following the Dart style guide, this should b
yjbanov
2016/02/17 23:51:43
Done.
| |
362 | |
300 VMIsolate._(Scope scope, Map json) | 363 VMIsolate._(Scope scope, Map json) |
301 : startTime = new DateTime.fromMillisecondsSinceEpoch( | 364 : startTime = new DateTime.fromMillisecondsSinceEpoch( |
302 // Prior to v3.0, this was emitted as a double rather than an int. | 365 // Prior to v3.0, this was emitted as a double rather than an int. |
303 json["startTime"].round()), | 366 json["startTime"].round()), |
304 livePorts = json["livePorts"], | 367 livePorts = json["livePorts"], |
305 pauseOnExit = json["pauseOnExit"], | 368 pauseOnExit = json["pauseOnExit"], |
306 pauseEvent = newVMPauseEvent(scope, json["pauseEvent"]), | 369 pauseEvent = newVMPauseEvent(scope, json["pauseEvent"]), |
307 error = newVMError(scope, json["error"]), | 370 error = newVMError(scope, json["error"]), |
308 breakpoints = new UnmodifiableListView(json["breakpoints"] | 371 breakpoints = new UnmodifiableListView(json["breakpoints"] |
309 .map((breakpoint) => newVMBreakpoint(scope, breakpoint)) | 372 .map((breakpoint) => newVMBreakpoint(scope, breakpoint)) |
310 .toList()), | 373 .toList()), |
374 extensionRPCs = json["extensionRPCs"] != null | |
375 ? new UnmodifiableListView(json["extensionRPCs"]) | |
376 : const <String>[], // it's null when no extensions are registered | |
nweiz
2016/02/17 21:35:41
Hmm, this is not what's documented. I've filed htt
yjbanov
2016/02/17 23:51:43
Done.
| |
311 super._(scope, json); | 377 super._(scope, json); |
312 } | 378 } |
313 | 379 |
314 /// A full isolate on the remote VM that's ready to run code. | 380 /// A full isolate on the remote VM that's ready to run code. |
315 /// | 381 /// |
316 /// The VM service exposes isolates very early, before their contents are | 382 /// The VM service exposes isolates very early, before their contents are |
317 /// fully-loaded. These in-progress isolates, represented by plain [VMIsolate] | 383 /// fully-loaded. These in-progress isolates, represented by plain [VMIsolate] |
318 /// instances, have limited amounts of metadata available. Only once they're | 384 /// instances, have limited amounts of metadata available. Only once they're |
319 /// runnable is the full suite of metadata available. | 385 /// runnable is the full suite of metadata available. |
320 /// | 386 /// |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
358 | 424 |
359 /// The isolate continues until it exits the current function. | 425 /// The isolate continues until it exits the current function. |
360 static const out = const VMStep._("Out"); | 426 static const out = const VMStep._("Out"); |
361 | 427 |
362 /// The string name of the step type. | 428 /// The string name of the step type. |
363 final String _value; | 429 final String _value; |
364 | 430 |
365 const VMStep._(this._value); | 431 const VMStep._(this._value); |
366 | 432 |
367 String toString() => _value; | 433 String toString() => _value; |
368 } | 434 } |
435 | |
436 /// An event posted via `postEvent` from the `dart:developer` package. | |
437 class VMExtensionEvent { | |
438 /// Event kind. | |
439 final String kind; | |
440 | |
441 /// Event data. | |
442 final Map data; | |
443 | |
444 VMExtensionEvent._(this.kind, this.data); | |
445 } | |
446 | |
447 /// Stream data handler that picks extension events based on event kind. | |
448 class _VMExtensionEventSelector { | |
449 /// The kind of events the subscriber is interested in. | |
450 final String kind; | |
451 | |
452 /// Whether [kind] must match as prefix. | |
453 final bool prefix; | |
454 | |
455 _VMExtensionEventSelector(this.kind, this.prefix); | |
456 | |
457 call(VMExtensionEvent event, EventSink<VMExtensionEvent> sink) { | |
458 if (event.kind == kind || (prefix && event.kind.startsWith(kind))) { | |
459 sink.add(event); | |
460 } | |
461 } | |
462 } | |
OLD | NEW |