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 library dart._vmservice; | 5 library dart._vmservice; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 import 'dart:convert'; | 9 import 'dart:convert'; |
10 import 'dart:isolate'; | 10 import 'dart:isolate'; |
11 import 'dart:typed_data'; | 11 import 'dart:typed_data'; |
12 | 12 |
13 part 'asset.dart'; | 13 part 'asset.dart'; |
14 part 'client.dart'; | 14 part 'client.dart'; |
15 part 'constants.dart'; | 15 part 'constants.dart'; |
16 part 'running_isolate.dart'; | 16 part 'running_isolate.dart'; |
17 part 'running_isolates.dart'; | 17 part 'running_isolates.dart'; |
18 part 'message.dart'; | 18 part 'message.dart'; |
19 part 'message_router.dart'; | 19 part 'message_router.dart'; |
20 | 20 |
21 final RawReceivePort isolateLifecyclePort = new RawReceivePort(); | 21 final RawReceivePort isolateLifecyclePort = new RawReceivePort(); |
22 final RawReceivePort scriptLoadPort = new RawReceivePort(); | 22 final RawReceivePort scriptLoadPort = new RawReceivePort(); |
23 | 23 |
24 typedef ShutdownCallback(); | |
25 | |
26 // These must be kept in sync with the declarations in vm/json_stream.h. | 24 // These must be kept in sync with the declarations in vm/json_stream.h. |
27 const kInvalidParams = -32602; | 25 const kInvalidParams = -32602; |
28 const kInternalError = -32603; | 26 const kInternalError = -32603; |
29 const kStreamAlreadySubscribed = 103; | 27 const kStreamAlreadySubscribed = 103; |
30 const kStreamNotSubscribed = 104; | 28 const kStreamNotSubscribed = 104; |
31 | 29 |
32 var _errorMessages = { | 30 var _errorMessages = { |
33 kInvalidParams: 'Invalid params', | 31 kInvalidParams: 'Invalid params', |
34 kInternalError: 'Internal error', | 32 kInternalError: 'Internal error', |
35 kStreamAlreadySubscribed: 'Stream already subscribed', | 33 kStreamAlreadySubscribed: 'Stream already subscribed', |
(...skipping 19 matching lines...) Expand all Loading... |
55 | 53 |
56 String encodeResult(Message message, Map result) { | 54 String encodeResult(Message message, Map result) { |
57 var response = { | 55 var response = { |
58 'jsonrpc': '2.0', | 56 'jsonrpc': '2.0', |
59 'id' : message.serial, | 57 'id' : message.serial, |
60 'result' : result, | 58 'result' : result, |
61 }; | 59 }; |
62 return JSON.encode(response); | 60 return JSON.encode(response); |
63 } | 61 } |
64 | 62 |
| 63 const shortDelay = const Duration(milliseconds: 10); |
| 64 |
| 65 /// Called when the server should be started. |
| 66 typedef Future ServerStartCallback(); |
| 67 |
| 68 /// Called when the server should be stopped. |
| 69 typedef Future ServerStopCallback(); |
| 70 |
| 71 /// Called when the service is exiting. |
| 72 typedef Future CleanupCallback(); |
| 73 |
| 74 /// Hooks that are setup by the embedder. |
| 75 class VMServiceEmbedderHooks { |
| 76 static ServerStartCallback serverStart; |
| 77 static ServerStopCallback serverStop; |
| 78 static CleanupCallback cleanup; |
| 79 } |
65 | 80 |
66 class VMService extends MessageRouter { | 81 class VMService extends MessageRouter { |
67 static VMService _instance; | 82 static VMService _instance; |
68 | 83 |
69 /// Collection of currently connected clients. | 84 /// Collection of currently connected clients. |
70 final Set<Client> clients = new Set<Client>(); | 85 final Set<Client> clients = new Set<Client>(); |
71 | 86 |
72 /// Collection of currently running isolates. | 87 /// Collection of currently running isolates. |
73 RunningIsolates runningIsolates = new RunningIsolates(); | 88 RunningIsolates runningIsolates = new RunningIsolates(); |
74 | 89 |
75 /// A port used to receive events from the VM. | 90 /// A port used to receive events from the VM. |
76 final RawReceivePort eventPort; | 91 final RawReceivePort eventPort; |
77 | 92 |
78 ShutdownCallback onShutdown; | |
79 | |
80 void _addClient(Client client) { | 93 void _addClient(Client client) { |
81 assert(client.streams.isEmpty); | 94 assert(client.streams.isEmpty); |
82 clients.add(client); | 95 clients.add(client); |
83 } | 96 } |
84 | 97 |
85 void _removeClient(Client client) { | 98 void _removeClient(Client client) { |
86 clients.remove(client); | 99 clients.remove(client); |
87 for (var streamId in client.streams) { | 100 for (var streamId in client.streams) { |
88 if (!_isAnyClientSubscribed(streamId)) { | 101 if (!_isAnyClientSubscribed(streamId)) { |
89 _vmCancelStream(streamId); | 102 _vmCancelStream(streamId); |
(...skipping 18 matching lines...) Expand all Loading... |
108 switch (code) { | 121 switch (code) { |
109 case Constants.ISOLATE_STARTUP_MESSAGE_ID: | 122 case Constants.ISOLATE_STARTUP_MESSAGE_ID: |
110 runningIsolates.isolateStartup(portId, sp, name); | 123 runningIsolates.isolateStartup(portId, sp, name); |
111 break; | 124 break; |
112 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: | 125 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: |
113 runningIsolates.isolateShutdown(portId, sp); | 126 runningIsolates.isolateShutdown(portId, sp); |
114 break; | 127 break; |
115 } | 128 } |
116 } | 129 } |
117 | 130 |
118 void _exit() { | 131 Future _exit() async { |
| 132 // Stop the server. |
| 133 if (VMServiceEmbedderHooks.serverStop != null) { |
| 134 await VMServiceEmbedderHooks.serverStop(); |
| 135 } |
| 136 |
| 137 // Close receive ports. |
119 isolateLifecyclePort.close(); | 138 isolateLifecyclePort.close(); |
120 scriptLoadPort.close(); | 139 scriptLoadPort.close(); |
| 140 |
121 // Create a copy of the set as a list because client.disconnect() will | 141 // Create a copy of the set as a list because client.disconnect() will |
122 // alter the connected clients set. | 142 // alter the connected clients set. |
123 var clientsList = clients.toList(); | 143 var clientsList = clients.toList(); |
124 for (var client in clientsList) { | 144 for (var client in clientsList) { |
125 client.disconnect(); | 145 client.disconnect(); |
126 } | 146 } |
127 // Call embedder shutdown hook after the internal shutdown. | 147 if (VMServiceEmbedderHooks.cleanup != null) { |
128 if (onShutdown != null) { | 148 await VMServiceEmbedderHooks.cleanup(); |
129 onShutdown(); | |
130 } | 149 } |
| 150 // Notify the VM that we have exited. |
131 _onExit(); | 151 _onExit(); |
132 } | 152 } |
133 | 153 |
134 void messageHandler(message) { | 154 void messageHandler(message) { |
135 if (message is List) { | 155 if (message is List) { |
136 if (message.length == 2) { | 156 if (message.length == 2) { |
137 // This is an event. | 157 // This is an event. |
138 assert(message[0] is String); | 158 assert(message[0] is String); |
139 assert(message[1] is String || message[1] is Uint8List); | 159 assert(message[1] is String || message[1] is Uint8List); |
140 _eventMessageHandler(message); | 160 _eventMessageHandler(message); |
141 return; | 161 return; |
142 } | 162 } |
143 if (message.length == 1) { | 163 if (message.length == 1) { |
144 // This is a control message directing the vm service to exit. | 164 // This is a control message directing the vm service to exit. |
145 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); | 165 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); |
146 _exit(); | 166 _exit(); |
147 return; | 167 return; |
148 } | 168 } |
149 if (message.length == 4) { | 169 if (message.length == 4) { |
150 // This is a message informing us of the birth or death of an | 170 // This is a message informing us of the birth or death of an |
151 // isolate. | 171 // isolate. |
152 _controlMessageHandler(message[0], message[1], message[2], message[3]); | 172 _controlMessageHandler(message[0], message[1], message[2], message[3]); |
153 return; | 173 return; |
154 } | 174 } |
155 } | 175 } |
156 print('Internal vm-service error: ignoring illegal message: $message'); | 176 print('Internal vm-service error: ignoring illegal message: $message'); |
157 } | 177 } |
158 | 178 |
159 void _notSupported(_) { | |
160 throw new UnimplementedError('Service script loading not supported.'); | |
161 } | |
162 | |
163 VMService._internal() | 179 VMService._internal() |
164 : eventPort = isolateLifecyclePort { | 180 : eventPort = isolateLifecyclePort { |
165 scriptLoadPort.handler = _notSupported; | |
166 eventPort.handler = messageHandler; | 181 eventPort.handler = messageHandler; |
167 } | 182 } |
168 | 183 |
169 factory VMService() { | 184 factory VMService() { |
170 if (VMService._instance == null) { | 185 if (VMService._instance == null) { |
171 VMService._instance = new VMService._internal(); | 186 VMService._instance = new VMService._internal(); |
172 _onStart(); | 187 _onStart(); |
173 } | 188 } |
174 return _instance; | 189 return _instance; |
175 } | 190 } |
176 | 191 |
177 void _clientCollection(Message message) { | |
178 var members = []; | |
179 var result = {}; | |
180 clients.forEach((client) { | |
181 members.add(client.toJson()); | |
182 }); | |
183 result['type'] = 'ClientList'; | |
184 result['members'] = members; | |
185 message.setResponse(JSON.encode(result)); | |
186 } | |
187 | |
188 bool _isAnyClientSubscribed(String streamId) { | 192 bool _isAnyClientSubscribed(String streamId) { |
189 for (var client in clients) { | 193 for (var client in clients) { |
190 if (client.streams.contains(streamId)) { | 194 if (client.streams.contains(streamId)) { |
191 return true; | 195 return true; |
192 } | 196 } |
193 } | 197 } |
194 return false; | 198 return false; |
195 } | 199 } |
196 | 200 |
197 Future<String> _streamListen(Message message) async { | 201 Future<String> _streamListen(Message message) async { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
286 | 290 |
287 // Encode the entire crash dump. | 291 // Encode the entire crash dump. |
288 return encodeResult(message, responses); | 292 return encodeResult(message, responses); |
289 } | 293 } |
290 | 294 |
291 Future<String> route(Message message) { | 295 Future<String> route(Message message) { |
292 if (message.completed) { | 296 if (message.completed) { |
293 return message.response; | 297 return message.response; |
294 } | 298 } |
295 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. | 299 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. |
296 if ((message.path.length == 1) && (message.path[0] == 'clients')) { | |
297 _clientCollection(message); | |
298 return message.response; | |
299 } | |
300 if (message.method == '_getCrashDump') { | 300 if (message.method == '_getCrashDump') { |
301 return _getCrashDump(message); | 301 return _getCrashDump(message); |
302 } | 302 } |
303 if (message.method == 'streamListen') { | 303 if (message.method == 'streamListen') { |
304 return _streamListen(message); | 304 return _streamListen(message); |
305 } | 305 } |
306 if (message.method == 'streamCancel') { | 306 if (message.method == 'streamCancel') { |
307 return _streamCancel(message); | 307 return _streamCancel(message); |
308 } | 308 } |
309 if (message.params['isolateId'] != null) { | 309 if (message.params['isolateId'] != null) { |
310 return runningIsolates.route(message); | 310 return runningIsolates.route(message); |
311 } | 311 } |
312 return message.sendToVM(); | 312 return message.sendToVM(); |
313 } | 313 } |
314 } | 314 } |
315 | 315 |
316 RawReceivePort boot() { | 316 RawReceivePort boot() { |
317 // Return the port we expect isolate startup and shutdown messages on. | 317 // Return the port we expect isolate startup and shutdown messages on. |
318 return isolateLifecyclePort; | 318 return isolateLifecyclePort; |
319 } | 319 } |
320 | 320 |
321 void _registerIsolate(int port_id, SendPort sp, String name) { | 321 void _registerIsolate(int port_id, SendPort sp, String name) { |
322 var service = new VMService(); | 322 var service = new VMService(); |
323 service.runningIsolates.isolateStartup(port_id, sp, name); | 323 service.runningIsolates.isolateStartup(port_id, sp, name); |
324 } | 324 } |
325 | 325 |
| 326 /// Notify the VM that the service is running. |
326 external void _onStart(); | 327 external void _onStart(); |
327 | 328 |
| 329 /// Notify the VM that the service is no longer running. |
328 external void _onExit(); | 330 external void _onExit(); |
329 | 331 |
| 332 /// Notify the VM that the server's address has changed. |
| 333 external void onServerAddressChange(String address); |
| 334 |
| 335 /// Subscribe to a service stream. |
330 external bool _vmListenStream(String streamId); | 336 external bool _vmListenStream(String streamId); |
331 | 337 |
| 338 /// Cancel a subscription to a service stream. |
332 external void _vmCancelStream(String streamId); | 339 external void _vmCancelStream(String streamId); |
333 | 340 |
| 341 /// Get the bytes to the tar archive. |
334 external Uint8List _requestAssets(); | 342 external Uint8List _requestAssets(); |
OLD | NEW |