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