OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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:convert'; |
6 import 'dart:isolate'; | 7 import 'dart:isolate'; |
7 | 8 |
| 9 import 'package:analyzer/instrumentation/instrumentation.dart'; |
8 import 'package:analyzer_plugin/channel/channel.dart'; | 10 import 'package:analyzer_plugin/channel/channel.dart'; |
9 import 'package:analyzer_plugin/protocol/protocol.dart'; | 11 import 'package:analyzer_plugin/protocol/protocol.dart'; |
10 | 12 |
11 /** | 13 /** |
12 * The object that allows a [ServerPlugin] to receive [Request]s and to return | 14 * The object that allows a [ServerPlugin] to receive [Request]s and to return |
13 * both [Response]s and [Notification]s. It communicates with the analysis | 15 * both [Response]s and [Notification]s. It communicates with the analysis |
14 * server by passing data to the server's main isolate. | 16 * server by passing data to the server's main isolate. |
15 */ | 17 */ |
16 class PluginIsolateChannel implements PluginCommunicationChannel { | 18 class PluginIsolateChannel implements PluginCommunicationChannel { |
17 /** | 19 /** |
(...skipping 25 matching lines...) Expand all Loading... |
43 _subscription.cancel(); | 45 _subscription.cancel(); |
44 _subscription = null; | 46 _subscription = null; |
45 } | 47 } |
46 } | 48 } |
47 | 49 |
48 @override | 50 @override |
49 void listen(void onRequest(Request request), | 51 void listen(void onRequest(Request request), |
50 {Function onError, void onDone()}) { | 52 {Function onError, void onDone()}) { |
51 void onData(data) { | 53 void onData(data) { |
52 Map<String, Object> requestMap = data; | 54 Map<String, Object> requestMap = data; |
| 55 // print('[plugin] Received request: ${JSON.encode(requestMap)}'); |
53 Request request = new Request.fromJson(requestMap); | 56 Request request = new Request.fromJson(requestMap); |
54 if (request != null) { | 57 if (request != null) { |
55 onRequest(request); | 58 onRequest(request); |
56 } | 59 } |
57 } | 60 } |
58 | 61 |
59 if (_subscription != null) { | 62 if (_subscription != null) { |
60 throw new StateError('Only one listener is allowed per channel'); | 63 throw new StateError('Only one listener is allowed per channel'); |
61 } | 64 } |
62 _subscription = _receivePort.listen(onData, | 65 _subscription = _receivePort.listen(onData, |
63 onError: onError, onDone: onDone, cancelOnError: false); | 66 onError: onError, onDone: onDone, cancelOnError: false); |
64 } | 67 } |
65 | 68 |
66 @override | 69 @override |
67 void sendNotification(Notification notification) { | 70 void sendNotification(Notification notification) { |
68 _sendPort.send(notification.toJson()); | 71 Map<String, Object> json = notification.toJson(); |
| 72 // print('[plugin] Send notification: ${JSON.encode(json)}'); |
| 73 _sendPort.send(json); |
69 } | 74 } |
70 | 75 |
71 @override | 76 @override |
72 void sendResponse(Response response) { | 77 void sendResponse(Response response) { |
73 _sendPort.send(response.toJson()); | 78 Map<String, Object> json = response.toJson(); |
| 79 // print('[plugin] Send response: ${JSON.encode(json)}'); |
| 80 _sendPort.send(json); |
74 } | 81 } |
75 } | 82 } |
76 | 83 |
77 /** | 84 /** |
78 * The communication channel that allows an analysis server to send [Request]s | 85 * The communication channel that allows an analysis server to send [Request]s |
79 * to, and to receive both [Response]s and [Notification]s from, a plugin. | 86 * to, and to receive both [Response]s and [Notification]s from, a plugin. |
80 */ | 87 */ |
81 class ServerIsolateChannel implements ServerCommunicationChannel { | 88 class ServerIsolateChannel implements ServerCommunicationChannel { |
82 /** | 89 /** |
83 * The URI for the plugin that will be run in the isolate that this channel | 90 * The URI for the Dart file that will be run in the isolate that this channel |
84 * communicates with. | 91 * communicates with. |
85 */ | 92 */ |
86 final Uri uri; | 93 final Uri pluginUri; |
| 94 |
| 95 /** |
| 96 * The URI for the '.packages' file that will control how 'package:' URIs are |
| 97 * resolved. |
| 98 */ |
| 99 final Uri packagesUri; |
| 100 |
| 101 /** |
| 102 * The instrumentation service that is being used by the analysis server. |
| 103 */ |
| 104 final InstrumentationService instrumentationService; |
87 | 105 |
88 /** | 106 /** |
89 * The isolate in which the plugin is running, or `null` if the plugin has | 107 * The isolate in which the plugin is running, or `null` if the plugin has |
90 * not yet been started by invoking [listen]. | 108 * not yet been started by invoking [listen]. |
91 */ | 109 */ |
92 Isolate _isolate; | 110 Isolate _isolate; |
93 | 111 |
94 /** | 112 /** |
95 * The port used to send requests to the plugin, or `null` if the plugin has | 113 * The port used to send requests to the plugin, or `null` if the plugin has |
96 * not yet been started by invoking [listen]. | 114 * not yet been started by invoking [listen]. |
97 */ | 115 */ |
98 SendPort _sendPort; | 116 SendPort _sendPort; |
99 | 117 |
| 118 ReceivePort receivePort; |
| 119 |
| 120 ReceivePort errorPort; |
| 121 |
| 122 ReceivePort exitPort; |
| 123 |
100 /** | 124 /** |
101 * Initialize a newly created channel to communicate with an isolate running | 125 * Initialize a newly created channel to communicate with an isolate running |
102 * the code at the given [uri]. | 126 * the code at the given [uri]. |
103 */ | 127 */ |
104 ServerIsolateChannel(this.uri); | 128 ServerIsolateChannel( |
105 | 129 this.pluginUri, this.packagesUri, this.instrumentationService); |
106 @override | 130 @override |
107 void close() { | 131 void close() { |
108 // TODO(brianwilkerson) Is there anything useful to do here? | 132 receivePort?.close(); |
| 133 errorPort?.close(); |
| 134 exitPort?.close(); |
109 _isolate = null; | 135 _isolate = null; |
110 _sendPort = null; | 136 // _sendPort = null; |
| 137 // receivePort = null; |
| 138 // errorPort = null; |
| 139 // exitPort = null; |
111 } | 140 } |
112 | 141 |
113 @override | 142 @override |
114 Future<Null> listen(void onResponse(Response response), | 143 Future<Null> listen(void onResponse(Response response), |
115 void onNotification(Notification notification), | 144 void onNotification(Notification notification), |
116 {Function onError, void onDone()}) async { | 145 {Function onError, void onDone()}) async { |
117 if (_isolate != null) { | 146 if (_isolate != null) { |
118 throw new StateError('Cannot listen to the same channel more than once.'); | 147 throw new StateError('Cannot listen to the same channel more than once.'); |
119 } | 148 } |
120 ReceivePort receivePort = new ReceivePort(); | 149 receivePort = new ReceivePort(); |
121 ReceivePort errorPort; | |
122 if (onError != null) { | 150 if (onError != null) { |
123 errorPort = new ReceivePort(); | 151 errorPort = new ReceivePort(); |
124 errorPort.listen((error) { | 152 errorPort.listen((error) { |
125 onError(error); | 153 onError(error); |
126 }); | 154 }); |
127 } | 155 } |
128 ReceivePort exitPort; | |
129 if (onDone != null) { | 156 if (onDone != null) { |
130 exitPort = new ReceivePort(); | 157 exitPort = new ReceivePort(); |
131 exitPort.listen((_) { | 158 exitPort.listen((_) { |
132 onDone(); | 159 onDone(); |
133 }); | 160 }); |
134 } | 161 } |
135 _isolate = await Isolate.spawnUri(uri, <String>[], receivePort.sendPort, | 162 _isolate = await Isolate.spawnUri( |
136 automaticPackageResolution: true, | 163 pluginUri, <String>[], receivePort.sendPort, |
137 onError: errorPort?.sendPort, | 164 onError: errorPort?.sendPort, |
138 onExit: exitPort?.sendPort); | 165 onExit: exitPort?.sendPort, |
139 _sendPort = await receivePort.first as SendPort; | 166 packageConfig: packagesUri); |
| 167 Completer<Null> channelReady = new Completer<Null>(); |
140 receivePort.listen((dynamic input) { | 168 receivePort.listen((dynamic input) { |
141 if (input is Map) { | 169 if (input is SendPort) { |
| 170 // print('[server] Received send port'); |
| 171 _sendPort = input; |
| 172 channelReady.complete(null); |
| 173 } else if (input is Map) { |
142 if (input.containsKey('id') != null) { | 174 if (input.containsKey('id') != null) { |
| 175 String encodedInput = JSON.encode(input); |
| 176 // print('[server] Received response: $encodedInput'); |
| 177 instrumentationService.logPluginResponse(pluginUri, encodedInput); |
143 onResponse(new Response.fromJson(input)); | 178 onResponse(new Response.fromJson(input)); |
144 } else if (input.containsKey('event')) { | 179 } else if (input.containsKey('event')) { |
| 180 String encodedInput = JSON.encode(input); |
| 181 // print('[server] Received notification: $encodedInput'); |
| 182 instrumentationService.logPluginNotification(pluginUri, encodedInput); |
145 onNotification(new Notification.fromJson(input)); | 183 onNotification(new Notification.fromJson(input)); |
146 } | 184 } |
147 } | 185 } |
148 }); | 186 }); |
| 187 return channelReady.future; |
149 } | 188 } |
150 | 189 |
151 @override | 190 @override |
152 void sendRequest(Request request) { | 191 void sendRequest(Request request) { |
153 _sendPort.send(request.toJson()); | 192 Map<String, Object> json = request.toJson(); |
| 193 String encodedRequest = JSON.encode(json); |
| 194 // print('[server] Send request: $encodedRequest'); |
| 195 instrumentationService.logPluginRequest(pluginUri, encodedRequest); |
| 196 _sendPort.send(json); |
154 } | 197 } |
155 } | 198 } |
OLD | NEW |