| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'dart:async'; | |
| import 'dart:convert'; | |
| import 'dart:io'; | |
| class ServiceClient { | |
| Completer<dynamic>? isolateStartedId; | |
| Completer<dynamic>? isolatePausedId; | |
| Completer<dynamic>? isolateResumeId; | |
| ServiceClient( | |
| this.client, { | |
| this.isolateStartedId, | |
| this.isolatePausedId, | |
| this.isolateResumeId, | |
| }) { | |
| client.listen(_onData, onError: _onError, cancelOnError: true); | |
| } | |
| Future<Map<String, dynamic>> invokeRPC(String method, | |
| [Map<String, dynamic>? params]) async { | |
| final String key = _createKey(); | |
| final String request = json.encode(<String, dynamic>{ | |
| 'jsonrpc': '2.0', | |
| 'method': method, | |
| 'params': params == null ? <String, dynamic>{} : params, | |
| 'id': key, | |
| }); | |
| client.add(request); | |
| final Completer<Map<String, dynamic>> completer = | |
| Completer<Map<String, dynamic>>(); | |
| _outstandingRequests[key] = completer; | |
| print('-> $key ($method)'); | |
| return completer.future; | |
| } | |
| String _createKey() { | |
| final String key = '$_id'; | |
| _id++; | |
| return key; | |
| } | |
| void _onData(dynamic message) { | |
| final Map<String, dynamic> response = | |
| json.decode(message as String) as Map<String, dynamic>; | |
| final dynamic key = response['id']; | |
| if (key != null) { | |
| print('<- $key'); | |
| final dynamic completer = _outstandingRequests.remove(key); | |
| assert(completer != null); | |
| final dynamic result = response['result']; | |
| final dynamic error = response['error']; | |
| if (error != null) { | |
| assert(result == null); | |
| completer.completeError(error); | |
| } else { | |
| assert(result != null); | |
| completer.complete(result); | |
| } | |
| } else { | |
| if (response['method'] == 'streamNotify') { | |
| _onServiceEvent(response['params'] as Map<String, dynamic>?); | |
| } | |
| } | |
| } | |
| void _onServiceEvent(Map<String, dynamic>? params) { | |
| if (params == null) { | |
| return; | |
| } | |
| final Map<String, dynamic>? event = | |
| params['event'] as Map<String, dynamic>?; | |
| if (event == null || event['type'] != 'Event') { | |
| return; | |
| } | |
| final dynamic isolateId = event['isolate']['id']; | |
| switch (params['streamId']) { | |
| case 'Isolate': | |
| if (event['kind'] == 'IsolateStart') { | |
| isolateStartedId?.complete(isolateId); | |
| } | |
| break; | |
| case 'Debug': | |
| switch (event['kind']) { | |
| case 'Resume': | |
| isolateResumeId?.complete(isolateId); | |
| break; | |
| case 'PauseStart': | |
| isolatePausedId?.complete(isolateId); | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| void _onError(dynamic error) { | |
| print('WebSocket error: $error'); | |
| } | |
| final WebSocket client; | |
| final Map<String, Completer<dynamic>> _outstandingRequests = | |
| <String, Completer<dynamic>>{}; | |
| int _id = 1; | |
| } |