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 'devfs.dart'; |
15 part 'constants.dart'; | 16 part 'constants.dart'; |
16 part 'running_isolate.dart'; | 17 part 'running_isolate.dart'; |
17 part 'running_isolates.dart'; | 18 part 'running_isolates.dart'; |
18 part 'message.dart'; | 19 part 'message.dart'; |
19 part 'message_router.dart'; | 20 part 'message_router.dart'; |
20 | 21 |
21 final RawReceivePort isolateLifecyclePort = new RawReceivePort(); | 22 final RawReceivePort isolateLifecyclePort = new RawReceivePort(); |
22 final RawReceivePort scriptLoadPort = new RawReceivePort(); | 23 final RawReceivePort scriptLoadPort = new RawReceivePort(); |
23 | 24 |
24 abstract class IsolateEmbedderData { | 25 abstract class IsolateEmbedderData { |
25 void cleanup(); | 26 void cleanup(); |
26 } | 27 } |
27 | 28 |
28 // This is for use by the embedder. It is a map from the isolateId to | 29 // This is for use by the embedder. It is a map from the isolateId to |
29 // anything implementing IsolateEmbedderData. When an isolate goes away, | 30 // anything implementing IsolateEmbedderData. When an isolate goes away, |
30 // the cleanup method will be invoked after being removed from the map. | 31 // the cleanup method will be invoked after being removed from the map. |
31 final Map<int, IsolateEmbedderData> isolateEmbedderData = | 32 final Map<int, IsolateEmbedderData> isolateEmbedderData = |
32 new Map<int, IsolateEmbedderData>(); | 33 new Map<int, IsolateEmbedderData>(); |
33 | 34 |
34 // These must be kept in sync with the declarations in vm/json_stream.h. | 35 // These must be kept in sync with the declarations in vm/json_stream.h. |
35 const kInvalidParams = -32602; | 36 const kInvalidParams = -32602; |
36 const kInternalError = -32603; | 37 const kInternalError = -32603; |
37 const kStreamAlreadySubscribed = 103; | 38 const kFeatureDisabled = 100; |
38 const kStreamNotSubscribed = 104; | 39 const kStreamAlreadySubscribed = 103; |
| 40 const kStreamNotSubscribed = 104; |
| 41 const kFileSystemAlreadyExists = 1001; |
| 42 const kFileSystemDoesNotExist = 1002; |
| 43 const kFileDoesNotExist = 1003; |
39 | 44 |
40 var _errorMessages = { | 45 var _errorMessages = { |
41 kInvalidParams: 'Invalid params', | 46 kInvalidParams: 'Invalid params', |
42 kInternalError: 'Internal error', | 47 kInternalError: 'Internal error', |
| 48 kFeatureDisabled: 'Feature is disabled', |
43 kStreamAlreadySubscribed: 'Stream already subscribed', | 49 kStreamAlreadySubscribed: 'Stream already subscribed', |
44 kStreamNotSubscribed: 'Stream not subscribed', | 50 kStreamNotSubscribed: 'Stream not subscribed', |
| 51 kFileSystemAlreadyExists: 'File system already exists', |
| 52 kFileSystemDoesNotExist: 'File system does not exist', |
| 53 kFileDoesNotExist: 'File does not exist', |
45 }; | 54 }; |
46 | 55 |
47 String encodeRpcError(Message message, int code, {String details}) { | 56 String encodeRpcError(Message message, int code, {String details}) { |
48 var response = { | 57 var response = { |
49 'jsonrpc': '2.0', | 58 'jsonrpc': '2.0', |
50 'id' : message.serial, | 59 'id' : message.serial, |
51 'error' : { | 60 'error' : { |
52 'code': code, | 61 'code': code, |
53 'message': _errorMessages[code], | 62 'message': _errorMessages[code], |
54 }, | 63 }, |
55 }; | 64 }; |
56 if (details != null) { | 65 if (details != null) { |
57 response['error']['data'] = { | 66 response['error']['data'] = { |
58 'details': details, | 67 'details': details, |
59 }; | 68 }; |
60 } | 69 } |
61 return JSON.encode(response); | 70 return JSON.encode(response); |
62 } | 71 } |
63 | 72 |
| 73 String encodeMissingParamError(Message message, String param) { |
| 74 return encodeRpcError( |
| 75 message, kInvalidParams, |
| 76 details: "${message.method} expects the '${param}' parameter"); |
| 77 } |
| 78 |
| 79 String encodeInvalidParamError(Message message, String param) { |
| 80 var value = message.params[param]; |
| 81 return encodeRpcError( |
| 82 message, kInvalidParams, |
| 83 details: "${message.method}: invalid '${param}' parameter: ${value}"); |
| 84 } |
| 85 |
64 String encodeResult(Message message, Map result) { | 86 String encodeResult(Message message, Map result) { |
65 var response = { | 87 var response = { |
66 'jsonrpc': '2.0', | 88 'jsonrpc': '2.0', |
67 'id' : message.serial, | 89 'id' : message.serial, |
68 'result' : result, | 90 'result' : result, |
69 }; | 91 }; |
70 return JSON.encode(response); | 92 return JSON.encode(response); |
71 } | 93 } |
72 | 94 |
| 95 String encodeSuccess(Message message) { |
| 96 return encodeResult(message, { 'type': 'Success' }); |
| 97 } |
| 98 |
73 const shortDelay = const Duration(milliseconds: 10); | 99 const shortDelay = const Duration(milliseconds: 10); |
74 | 100 |
75 /// Called when the server should be started. | 101 /// Called when the server should be started. |
76 typedef Future ServerStartCallback(); | 102 typedef Future ServerStartCallback(); |
77 | 103 |
78 /// Called when the server should be stopped. | 104 /// Called when the server should be stopped. |
79 typedef Future ServerStopCallback(); | 105 typedef Future ServerStopCallback(); |
80 | 106 |
81 /// Called when the service is exiting. | 107 /// Called when the service is exiting. |
82 typedef Future CleanupCallback(); | 108 typedef Future CleanupCallback(); |
83 | 109 |
| 110 /// Called to create a temporary directory |
| 111 typedef Future<Uri> CreateTempDirCallback(String base); |
| 112 |
| 113 /// Called to delete a directory |
| 114 typedef Future DeleteDirCallback(Uri path); |
| 115 |
| 116 /// Called to write a file. |
| 117 typedef Future WriteFileCallback(Uri path, List<int> bytes); |
| 118 |
| 119 /// Called to read a file. |
| 120 typedef Future<List<int>> ReadFileCallback(Uri path); |
| 121 |
| 122 /// Called to list all files under some path. |
| 123 typedef Future<List<Map<String,String>>> ListFilesCallback(Uri path); |
| 124 |
84 /// Hooks that are setup by the embedder. | 125 /// Hooks that are setup by the embedder. |
85 class VMServiceEmbedderHooks { | 126 class VMServiceEmbedderHooks { |
86 static ServerStartCallback serverStart; | 127 static ServerStartCallback serverStart; |
87 static ServerStopCallback serverStop; | 128 static ServerStopCallback serverStop; |
88 static CleanupCallback cleanup; | 129 static CleanupCallback cleanup; |
| 130 static CreateTempDirCallback createTempDir; |
| 131 static DeleteDirCallback deleteDir; |
| 132 static WriteFileCallback writeFile; |
| 133 static ReadFileCallback readFile; |
| 134 static ListFilesCallback listFiles; |
89 } | 135 } |
90 | 136 |
91 class VMService extends MessageRouter { | 137 class VMService extends MessageRouter { |
92 static VMService _instance; | 138 static VMService _instance; |
93 | 139 |
94 /// Collection of currently connected clients. | 140 /// Collection of currently connected clients. |
95 final Set<Client> clients = new Set<Client>(); | 141 final Set<Client> clients = new Set<Client>(); |
96 | 142 |
97 /// Collection of currently running isolates. | 143 /// Collection of currently running isolates. |
98 RunningIsolates runningIsolates = new RunningIsolates(); | 144 RunningIsolates runningIsolates = new RunningIsolates(); |
99 | 145 |
100 /// A port used to receive events from the VM. | 146 /// A port used to receive events from the VM. |
101 final RawReceivePort eventPort; | 147 final RawReceivePort eventPort; |
102 | 148 |
| 149 final _devfs = new DevFS(); |
| 150 |
103 void _addClient(Client client) { | 151 void _addClient(Client client) { |
104 assert(client.streams.isEmpty); | 152 assert(client.streams.isEmpty); |
105 clients.add(client); | 153 clients.add(client); |
106 } | 154 } |
107 | 155 |
108 void _removeClient(Client client) { | 156 void _removeClient(Client client) { |
109 clients.remove(client); | 157 clients.remove(client); |
110 for (var streamId in client.streams) { | 158 for (var streamId in client.streams) { |
111 if (!_isAnyClientSubscribed(streamId)) { | 159 if (!_isAnyClientSubscribed(streamId)) { |
112 _vmCancelStream(streamId); | 160 _vmCancelStream(streamId); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 // Close receive ports. | 199 // Close receive ports. |
152 isolateLifecyclePort.close(); | 200 isolateLifecyclePort.close(); |
153 scriptLoadPort.close(); | 201 scriptLoadPort.close(); |
154 | 202 |
155 // Create a copy of the set as a list because client.disconnect() will | 203 // Create a copy of the set as a list because client.disconnect() will |
156 // alter the connected clients set. | 204 // alter the connected clients set. |
157 var clientsList = clients.toList(); | 205 var clientsList = clients.toList(); |
158 for (var client in clientsList) { | 206 for (var client in clientsList) { |
159 client.disconnect(); | 207 client.disconnect(); |
160 } | 208 } |
| 209 _devfs.cleanup(); |
161 if (VMServiceEmbedderHooks.cleanup != null) { | 210 if (VMServiceEmbedderHooks.cleanup != null) { |
162 await VMServiceEmbedderHooks.cleanup(); | 211 await VMServiceEmbedderHooks.cleanup(); |
163 } | 212 } |
164 // Notify the VM that we have exited. | 213 // Notify the VM that we have exited. |
165 _onExit(); | 214 _onExit(); |
166 } | 215 } |
167 | 216 |
168 void messageHandler(message) { | 217 void messageHandler(message) { |
169 if (message is List) { | 218 if (message is List) { |
170 if (message.length == 2) { | 219 if (message.length == 2) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 } | 270 } |
222 if (!_isAnyClientSubscribed(streamId)) { | 271 if (!_isAnyClientSubscribed(streamId)) { |
223 if (!_vmListenStream(streamId)) { | 272 if (!_vmListenStream(streamId)) { |
224 return encodeRpcError( | 273 return encodeRpcError( |
225 message, kInvalidParams, | 274 message, kInvalidParams, |
226 details:"streamListen: invalid 'streamId' parameter: ${streamId}"); | 275 details:"streamListen: invalid 'streamId' parameter: ${streamId}"); |
227 } | 276 } |
228 } | 277 } |
229 client.streams.add(streamId); | 278 client.streams.add(streamId); |
230 | 279 |
231 var result = { 'type' : 'Success' }; | 280 return encodeSuccess(message); |
232 return encodeResult(message, result); | |
233 } | 281 } |
234 | 282 |
235 Future<String> _streamCancel(Message message) async { | 283 Future<String> _streamCancel(Message message) async { |
236 var client = message.client; | 284 var client = message.client; |
237 var streamId = message.params['streamId']; | 285 var streamId = message.params['streamId']; |
238 | 286 |
239 if (!client.streams.contains(streamId)) { | 287 if (!client.streams.contains(streamId)) { |
240 return encodeRpcError(message, kStreamNotSubscribed); | 288 return encodeRpcError(message, kStreamNotSubscribed); |
241 } | 289 } |
242 client.streams.remove(streamId); | 290 client.streams.remove(streamId); |
243 if (!_isAnyClientSubscribed(streamId)) { | 291 if (!_isAnyClientSubscribed(streamId)) { |
244 _vmCancelStream(streamId); | 292 _vmCancelStream(streamId); |
245 } | 293 } |
246 | 294 |
247 var result = { 'type' : 'Success' }; | 295 return encodeSuccess(message); |
248 return encodeResult(message, result); | |
249 } | 296 } |
250 | 297 |
251 // TODO(johnmccutchan): Turn this into a command line tool that uses the | 298 // TODO(johnmccutchan): Turn this into a command line tool that uses the |
252 // service library. | 299 // service library. |
253 Future<String> _getCrashDump(Message message) async { | 300 Future<String> _getCrashDump(Message message) async { |
254 var client = message.client; | 301 var client = message.client; |
255 final perIsolateRequests = [ | 302 final perIsolateRequests = [ |
256 // ?isolateId=<isolate id> will be appended to each of these requests. | 303 // ?isolateId=<isolate id> will be appended to each of these requests. |
257 // Isolate information. | 304 // Isolate information. |
258 Uri.parse('getIsolate'), | 305 Uri.parse('getIsolate'), |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. | 360 // TODO(turnidge): Update to json rpc. BEFORE SUBMIT. |
314 if (message.method == '_getCrashDump') { | 361 if (message.method == '_getCrashDump') { |
315 return _getCrashDump(message); | 362 return _getCrashDump(message); |
316 } | 363 } |
317 if (message.method == 'streamListen') { | 364 if (message.method == 'streamListen') { |
318 return _streamListen(message); | 365 return _streamListen(message); |
319 } | 366 } |
320 if (message.method == 'streamCancel') { | 367 if (message.method == 'streamCancel') { |
321 return _streamCancel(message); | 368 return _streamCancel(message); |
322 } | 369 } |
| 370 if (_devfs.shouldHandleMessage(message)) { |
| 371 return _devfs.handleMessage(message); |
| 372 } |
323 if (message.params['isolateId'] != null) { | 373 if (message.params['isolateId'] != null) { |
324 return runningIsolates.route(message); | 374 return runningIsolates.route(message); |
325 } | 375 } |
326 return message.sendToVM(); | 376 return message.sendToVM(); |
327 } | 377 } |
328 } | 378 } |
329 | 379 |
330 RawReceivePort boot() { | 380 RawReceivePort boot() { |
331 // Return the port we expect isolate startup and shutdown messages on. | 381 // Return the port we expect isolate startup and shutdown messages on. |
332 return isolateLifecyclePort; | 382 return isolateLifecyclePort; |
(...skipping 14 matching lines...) Expand all Loading... |
347 external void onServerAddressChange(String address); | 397 external void onServerAddressChange(String address); |
348 | 398 |
349 /// Subscribe to a service stream. | 399 /// Subscribe to a service stream. |
350 external bool _vmListenStream(String streamId); | 400 external bool _vmListenStream(String streamId); |
351 | 401 |
352 /// Cancel a subscription to a service stream. | 402 /// Cancel a subscription to a service stream. |
353 external void _vmCancelStream(String streamId); | 403 external void _vmCancelStream(String streamId); |
354 | 404 |
355 /// Get the bytes to the tar archive. | 405 /// Get the bytes to the tar archive. |
356 external Uint8List _requestAssets(); | 406 external Uint8List _requestAssets(); |
OLD | NEW |