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