| 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 |