| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 part of service; | 5 part of service; |
| 6 | 6 |
| 7 /// Helper function for canceling a Future<StreamSubscription>. |
| 8 Future cancelFutureSubscription( |
| 9 Future<StreamSubscription> subscriptionFuture) async { |
| 10 if (subscriptionFuture != null) { |
| 11 var subscription = await subscriptionFuture; |
| 12 return subscription.cancel(); |
| 13 } else { |
| 14 return null; |
| 15 } |
| 16 } |
| 17 |
| 7 /// An RpcException represents an exceptional event that happened | 18 /// An RpcException represents an exceptional event that happened |
| 8 /// while invoking an rpc. | 19 /// while invoking an rpc. |
| 9 abstract class RpcException implements Exception { | 20 abstract class RpcException implements Exception { |
| 10 RpcException(this.message); | 21 RpcException(this.message); |
| 11 | 22 |
| 12 String message; | 23 String message; |
| 13 } | 24 } |
| 14 | 25 |
| 15 /// A ServerRpcException represents an error returned by the VM. | 26 /// A ServerRpcException represents an error returned by the VM. |
| 16 class ServerRpcException extends RpcException { | 27 class ServerRpcException extends RpcException { |
| (...skipping 402 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 | 430 |
| 420 String toString() { | 431 String toString() { |
| 421 if (endTokenPos == null) { | 432 if (endTokenPos == null) { |
| 422 return '${script.name}:token(${tokenPos})'; | 433 return '${script.name}:token(${tokenPos})'; |
| 423 } else { | 434 } else { |
| 424 return '${script.name}:tokens(${tokenPos}-${endTokenPos})'; | 435 return '${script.name}:tokens(${tokenPos}-${endTokenPos})'; |
| 425 } | 436 } |
| 426 } | 437 } |
| 427 } | 438 } |
| 428 | 439 |
| 440 class _EventStreamState { |
| 441 VM _vm; |
| 442 String streamId; |
| 443 |
| 444 Function _onDone; |
| 445 |
| 446 // A list of all subscribed controllers for this stream. |
| 447 List _controllers = []; |
| 448 |
| 449 // Completes when the listen rpc is finished. |
| 450 Future _listenFuture; |
| 451 |
| 452 // Completes when then cancel rpc is finished. |
| 453 Future _cancelFuture; |
| 454 |
| 455 _EventStreamState(this._vm, this.streamId, this._onDone); |
| 456 |
| 457 Future _cancelController(StreamController controller) { |
| 458 _controllers.remove(controller); |
| 459 if (_controllers.isEmpty) { |
| 460 assert(_listenFuture != null); |
| 461 _listenFuture = null; |
| 462 _cancelFuture = _vm._streamCancel(streamId); |
| 463 _cancelFuture.then((_) { |
| 464 if (_controllers.isEmpty) { |
| 465 // No new listeners showed up during cancelation. |
| 466 _onDone(); |
| 467 } |
| 468 }); |
| 469 } |
| 470 // No need to wait for _cancelFuture here. |
| 471 return new Future.value(null); |
| 472 } |
| 473 |
| 474 Future<Stream> addStream() async { |
| 475 var controller; |
| 476 controller = new StreamController( |
| 477 onCancel:() => _cancelController(controller)); |
| 478 _controllers.add(controller); |
| 479 if (_cancelFuture != null) { |
| 480 await _cancelFuture; |
| 481 } |
| 482 if (_listenFuture == null) { |
| 483 _listenFuture = _vm._streamListen(streamId); |
| 484 } |
| 485 await _listenFuture; |
| 486 return controller.stream; |
| 487 } |
| 488 |
| 489 void addEvent(ServiceEvent event) { |
| 490 for (var controller in _controllers) { |
| 491 controller.add(event); |
| 492 } |
| 493 } |
| 494 } |
| 495 |
| 429 /// State for a VM being inspected. | 496 /// State for a VM being inspected. |
| 430 abstract class VM extends ServiceObjectOwner { | 497 abstract class VM extends ServiceObjectOwner { |
| 431 @reflectable VM get vm => this; | 498 @reflectable VM get vm => this; |
| 432 @reflectable Isolate get isolate => null; | 499 @reflectable Isolate get isolate => null; |
| 433 | 500 |
| 434 // TODO(turnidge): The connection should not be stored in the VM object. | 501 // TODO(turnidge): The connection should not be stored in the VM object. |
| 435 bool get isDisconnected; | 502 bool get isDisconnected; |
| 436 | 503 |
| 437 // TODO(johnmccutchan): Ensure that isolates do not end up in _cache. | 504 // TODO(johnmccutchan): Ensure that isolates do not end up in _cache. |
| 438 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); | 505 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 452 @observable Duration get upTime => | 519 @observable Duration get upTime => |
| 453 (new DateTime.now().difference(startTime)); | 520 (new DateTime.now().difference(startTime)); |
| 454 | 521 |
| 455 VM() : super._empty(null) { | 522 VM() : super._empty(null) { |
| 456 name = 'vm'; | 523 name = 'vm'; |
| 457 vmName = 'vm'; | 524 vmName = 'vm'; |
| 458 _cache['vm'] = this; | 525 _cache['vm'] = this; |
| 459 update(toObservable({'id':'vm', 'type':'@VM'})); | 526 update(toObservable({'id':'vm', 'type':'@VM'})); |
| 460 } | 527 } |
| 461 | 528 |
| 462 final StreamController<ServiceEvent> events = | 529 void postServiceEvent(String streamId, Map response, ByteData data) { |
| 463 new StreamController.broadcast(); | |
| 464 | |
| 465 void postServiceEvent(Map response, ByteData data) { | |
| 466 var map = toObservable(response); | 530 var map = toObservable(response); |
| 467 assert(!map.containsKey('_data')); | 531 assert(!map.containsKey('_data')); |
| 468 if (data != null) { | 532 if (data != null) { |
| 469 map['_data'] = data; | 533 map['_data'] = data; |
| 470 } | 534 } |
| 471 if (map['type'] != 'Event') { | 535 if (map['type'] != 'Event') { |
| 472 Logger.root.severe( | 536 Logger.root.severe( |
| 473 "Expected 'Event' but found '${map['type']}'"); | 537 "Expected 'Event' but found '${map['type']}'"); |
| 474 return; | 538 return; |
| 475 } | 539 } |
| 476 | 540 |
| 477 var eventIsolate = map['isolate']; | 541 var eventIsolate = map['isolate']; |
| 542 var event; |
| 478 if (eventIsolate == null) { | 543 if (eventIsolate == null) { |
| 479 var event = new ServiceObject._fromMap(vm, map); | 544 event = new ServiceObject._fromMap(vm, map); |
| 480 events.add(event); | |
| 481 } else { | 545 } else { |
| 482 // getFromMap creates the Isolate if it hasn't been seen already. | 546 // getFromMap creates the Isolate if it hasn't been seen already. |
| 483 var isolate = getFromMap(map['isolate']); | 547 var isolate = getFromMap(map['isolate']); |
| 484 var event = new ServiceObject._fromMap(isolate, map); | 548 event = new ServiceObject._fromMap(isolate, map); |
| 485 if (event.kind == ServiceEvent.kIsolateExit) { | 549 if (event.kind == ServiceEvent.kIsolateExit) { |
| 486 _removeIsolate(isolate.id); | 550 _removeIsolate(isolate.id); |
| 487 } | 551 } |
| 488 isolate._onEvent(event); | 552 } |
| 489 events.add(event); | 553 var eventStream = _eventStreams[streamId]; |
| 554 if (eventStream != null) { |
| 555 eventStream.addEvent(event); |
| 556 } else { |
| 557 Logger.root.warning("Ignoring unexpected event on stream '${streamId}'"); |
| 490 } | 558 } |
| 491 } | 559 } |
| 492 | 560 |
| 493 void _removeIsolate(String isolateId) { | 561 void _removeIsolate(String isolateId) { |
| 494 assert(_isolateCache.containsKey(isolateId)); | 562 assert(_isolateCache.containsKey(isolateId)); |
| 495 _isolateCache.remove(isolateId); | 563 _isolateCache.remove(isolateId); |
| 496 notifyPropertyChange(#isolates, true, false); | 564 notifyPropertyChange(#isolates, true, false); |
| 497 } | 565 } |
| 498 | 566 |
| 499 void _removeDeadIsolates(List newIsolates) { | 567 void _removeDeadIsolates(List newIsolates) { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { | 647 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
| 580 var obj = new ServiceObject._fromMap(this, response); | 648 var obj = new ServiceObject._fromMap(this, response); |
| 581 if ((obj != null) && obj.canCache) { | 649 if ((obj != null) && obj.canCache) { |
| 582 String objId = obj.id; | 650 String objId = obj.id; |
| 583 _cache.putIfAbsent(objId, () => obj); | 651 _cache.putIfAbsent(objId, () => obj); |
| 584 } | 652 } |
| 585 return obj; | 653 return obj; |
| 586 }); | 654 }); |
| 587 } | 655 } |
| 588 | 656 |
| 657 void _dispatchEventToIsolate(ServiceEvent event) { |
| 658 var isolate = event.isolate; |
| 659 if (isolate != null) { |
| 660 isolate._onEvent(event); |
| 661 } |
| 662 } |
| 663 |
| 589 Future<ObservableMap> _fetchDirect() async { | 664 Future<ObservableMap> _fetchDirect() async { |
| 590 if (!loaded) { | 665 if (!loaded) { |
| 591 // TODO(turnidge): Instead of always listening to all streams, | 666 // The vm service relies on these events to keep the VM and |
| 592 // implement a stream abstraction in the service library so | 667 // Isolate types up to date. |
| 593 // that we only subscribe to the streams we want. | 668 await listenEventStream(kIsolateStream, _dispatchEventToIsolate); |
| 594 await _streamListen('Isolate'); | 669 await listenEventStream(kDebugStream, _dispatchEventToIsolate); |
| 595 await _streamListen('Debug'); | 670 await listenEventStream(_kGraphStream, _dispatchEventToIsolate); |
| 596 await _streamListen('GC'); | |
| 597 await _streamListen('_Echo'); | |
| 598 await _streamListen('_Graph'); | |
| 599 } | 671 } |
| 600 return await invokeRpcNoUpgrade('getVM', {}); | 672 return await invokeRpcNoUpgrade('getVM', {}); |
| 601 } | 673 } |
| 602 | 674 |
| 603 Future<ServiceObject> getFlagList() { | 675 Future<ServiceObject> getFlagList() { |
| 604 return invokeRpc('getFlagList', {}); | 676 return invokeRpc('getFlagList', {}); |
| 605 } | 677 } |
| 606 | 678 |
| 607 Future<ServiceObject> _streamListen(String streamId) { | 679 Future<ServiceObject> _streamListen(String streamId) { |
| 608 Map params = { | 680 Map params = { |
| 609 'streamId': streamId, | 681 'streamId': streamId, |
| 610 }; | 682 }; |
| 611 return invokeRpc('streamListen', params); | 683 return invokeRpc('streamListen', params); |
| 612 } | 684 } |
| 613 | 685 |
| 686 Future<ServiceObject> _streamCancel(String streamId) { |
| 687 Map params = { |
| 688 'streamId': streamId, |
| 689 }; |
| 690 return invokeRpc('streamCancel', params); |
| 691 } |
| 692 |
| 693 // A map from stream id to event stream state. |
| 694 Map<String,_EventStreamState> _eventStreams = {}; |
| 695 |
| 696 // Well-known stream ids. |
| 697 static const kIsolateStream = 'Isolate'; |
| 698 static const kDebugStream = 'Debug'; |
| 699 static const kGCStream = 'GC'; |
| 700 static const _kGraphStream = '_Graph'; |
| 701 |
| 702 /// Returns a single-subscription Stream object for a VM event stream. |
| 703 Future<Stream> getEventStream(String streamId) async { |
| 704 var eventStream = _eventStreams.putIfAbsent( |
| 705 streamId, () => new _EventStreamState( |
| 706 this, streamId, () => _eventStreams.remove(streamId))); |
| 707 return eventStream.addStream(); |
| 708 } |
| 709 |
| 710 /// Helper function for listening to an event stream. |
| 711 Future<StreamSubscription> listenEventStream(String streamId, |
| 712 Function function) async { |
| 713 var stream = await getEventStream(streamId); |
| 714 return stream.listen(function); |
| 715 } |
| 716 |
| 614 /// Force the VM to disconnect. | 717 /// Force the VM to disconnect. |
| 615 void disconnect(); | 718 void disconnect(); |
| 616 /// Completes when the VM first connects. | 719 /// Completes when the VM first connects. |
| 617 Future get onConnect; | 720 Future get onConnect; |
| 618 /// Completes when the VM disconnects or there was an error connecting. | 721 /// Completes when the VM disconnects or there was an error connecting. |
| 619 Future get onDisconnect; | 722 Future get onDisconnect; |
| 620 | 723 |
| 621 void _update(ObservableMap map, bool mapIsRef) { | 724 void _update(ObservableMap map, bool mapIsRef) { |
| 622 if (mapIsRef) { | 725 if (mapIsRef) { |
| 623 return; | 726 return; |
| (...skipping 2829 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3453 var v = list[i]; | 3556 var v = list[i]; |
| 3454 if ((v is ObservableMap) && _isServiceMap(v)) { | 3557 if ((v is ObservableMap) && _isServiceMap(v)) { |
| 3455 list[i] = owner.getFromMap(v); | 3558 list[i] = owner.getFromMap(v); |
| 3456 } else if (v is ObservableList) { | 3559 } else if (v is ObservableList) { |
| 3457 _upgradeObservableList(v, owner); | 3560 _upgradeObservableList(v, owner); |
| 3458 } else if (v is ObservableMap) { | 3561 } else if (v is ObservableMap) { |
| 3459 _upgradeObservableMap(v, owner); | 3562 _upgradeObservableMap(v, owner); |
| 3460 } | 3563 } |
| 3461 } | 3564 } |
| 3462 } | 3565 } |
| OLD | NEW |