| 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:developer' show ServiceProtocolInfo; | 10 import 'dart:developer' show ServiceProtocolInfo; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 // TODO(johnmccutchan): Enable the auth token and drop the origin check. | 44 // TODO(johnmccutchan): Enable the auth token and drop the origin check. |
| 45 final bool useAuthToken = const bool.fromEnvironment('DART_SERVICE_USE_AUTH'); | 45 final bool useAuthToken = const bool.fromEnvironment('DART_SERVICE_USE_AUTH'); |
| 46 | 46 |
| 47 // This is for use by the embedder. It is a map from the isolateId to | 47 // This is for use by the embedder. It is a map from the isolateId to |
| 48 // anything implementing IsolateEmbedderData. When an isolate goes away, | 48 // anything implementing IsolateEmbedderData. When an isolate goes away, |
| 49 // the cleanup method will be invoked after being removed from the map. | 49 // the cleanup method will be invoked after being removed from the map. |
| 50 final Map<int, IsolateEmbedderData> isolateEmbedderData = | 50 final Map<int, IsolateEmbedderData> isolateEmbedderData = |
| 51 new Map<int, IsolateEmbedderData>(); | 51 new Map<int, IsolateEmbedderData>(); |
| 52 | 52 |
| 53 // These must be kept in sync with the declarations in vm/json_stream.h. | 53 // These must be kept in sync with the declarations in vm/json_stream.h. |
| 54 const kInvalidParams = -32602; | 54 const kInvalidParams = -32602; |
| 55 const kInternalError = -32603; | 55 const kInternalError = -32603; |
| 56 const kFeatureDisabled = 100; | 56 const kFeatureDisabled = 100; |
| 57 const kStreamAlreadySubscribed = 103; | 57 const kStreamAlreadySubscribed = 103; |
| 58 const kStreamNotSubscribed = 104; | 58 const kStreamNotSubscribed = 104; |
| 59 const kFileSystemAlreadyExists = 1001; | 59 const kFileSystemAlreadyExists = 1001; |
| 60 const kFileSystemDoesNotExist = 1002; | 60 const kFileSystemDoesNotExist = 1002; |
| 61 const kFileDoesNotExist = 1003; | 61 const kFileDoesNotExist = 1003; |
| 62 | 62 |
| 63 var _errorMessages = { | 63 var _errorMessages = { |
| 64 kInvalidParams: 'Invalid params', | 64 kInvalidParams: 'Invalid params', |
| 65 kInternalError: 'Internal error', | 65 kInternalError: 'Internal error', |
| 66 kFeatureDisabled: 'Feature is disabled', | 66 kFeatureDisabled: 'Feature is disabled', |
| 67 kStreamAlreadySubscribed: 'Stream already subscribed', | 67 kStreamAlreadySubscribed: 'Stream already subscribed', |
| 68 kStreamNotSubscribed: 'Stream not subscribed', | 68 kStreamNotSubscribed: 'Stream not subscribed', |
| 69 kFileSystemAlreadyExists: 'File system already exists', | 69 kFileSystemAlreadyExists: 'File system already exists', |
| 70 kFileSystemDoesNotExist: 'File system does not exist', | 70 kFileSystemDoesNotExist: 'File system does not exist', |
| 71 kFileDoesNotExist: 'File does not exist', | 71 kFileDoesNotExist: 'File does not exist', |
| 72 }; | 72 }; |
| 73 | 73 |
| 74 String encodeRpcError(Message message, int code, {String details}) { | 74 String encodeRpcError(Message message, int code, {String details}) { |
| 75 var response = { | 75 var response = { |
| 76 'jsonrpc': '2.0', | 76 'jsonrpc': '2.0', |
| 77 'id' : message.serial, | 77 'id': message.serial, |
| 78 'error' : { | 78 'error': { |
| 79 'code': code, | 79 'code': code, |
| 80 'message': _errorMessages[code], | 80 'message': _errorMessages[code], |
| 81 }, | 81 }, |
| 82 }; | 82 }; |
| 83 if (details != null) { | 83 if (details != null) { |
| 84 response['error']['data'] = { | 84 response['error']['data'] = { |
| 85 'details': details, | 85 'details': details, |
| 86 }; | 86 }; |
| 87 } | 87 } |
| 88 return JSON.encode(response); | 88 return JSON.encode(response); |
| 89 } | 89 } |
| 90 | 90 |
| 91 String encodeMissingParamError(Message message, String param) { | 91 String encodeMissingParamError(Message message, String param) { |
| 92 return encodeRpcError( | 92 return encodeRpcError(message, kInvalidParams, |
| 93 message, kInvalidParams, | |
| 94 details: "${message.method} expects the '${param}' parameter"); | 93 details: "${message.method} expects the '${param}' parameter"); |
| 95 } | 94 } |
| 96 | 95 |
| 97 String encodeInvalidParamError(Message message, String param) { | 96 String encodeInvalidParamError(Message message, String param) { |
| 98 var value = message.params[param]; | 97 var value = message.params[param]; |
| 99 return encodeRpcError( | 98 return encodeRpcError(message, kInvalidParams, |
| 100 message, kInvalidParams, | |
| 101 details: "${message.method}: invalid '${param}' parameter: ${value}"); | 99 details: "${message.method}: invalid '${param}' parameter: ${value}"); |
| 102 } | 100 } |
| 103 | 101 |
| 104 String encodeResult(Message message, Map result) { | 102 String encodeResult(Message message, Map result) { |
| 105 var response = { | 103 var response = { |
| 106 'jsonrpc': '2.0', | 104 'jsonrpc': '2.0', |
| 107 'id' : message.serial, | 105 'id': message.serial, |
| 108 'result' : result, | 106 'result': result, |
| 109 }; | 107 }; |
| 110 return JSON.encode(response); | 108 return JSON.encode(response); |
| 111 } | 109 } |
| 112 | 110 |
| 113 String encodeSuccess(Message message) { | 111 String encodeSuccess(Message message) { |
| 114 return encodeResult(message, { 'type': 'Success' }); | 112 return encodeResult(message, {'type': 'Success'}); |
| 115 } | 113 } |
| 116 | 114 |
| 117 const shortDelay = const Duration(milliseconds: 10); | 115 const shortDelay = const Duration(milliseconds: 10); |
| 118 | 116 |
| 119 /// Called when the server should be started. | 117 /// Called when the server should be started. |
| 120 typedef Future ServerStartCallback(); | 118 typedef Future ServerStartCallback(); |
| 121 | 119 |
| 122 /// Called when the server should be stopped. | 120 /// Called when the server should be stopped. |
| 123 typedef Future ServerStopCallback(); | 121 typedef Future ServerStopCallback(); |
| 124 | 122 |
| 125 /// Called when the service is exiting. | 123 /// Called when the service is exiting. |
| 126 typedef Future CleanupCallback(); | 124 typedef Future CleanupCallback(); |
| 127 | 125 |
| 128 /// Called to create a temporary directory | 126 /// Called to create a temporary directory |
| 129 typedef Future<Uri> CreateTempDirCallback(String base); | 127 typedef Future<Uri> CreateTempDirCallback(String base); |
| 130 | 128 |
| 131 /// Called to delete a directory | 129 /// Called to delete a directory |
| 132 typedef Future DeleteDirCallback(Uri path); | 130 typedef Future DeleteDirCallback(Uri path); |
| 133 | 131 |
| 134 /// Called to write a file. | 132 /// Called to write a file. |
| 135 typedef Future WriteFileCallback(Uri path, List<int> bytes); | 133 typedef Future WriteFileCallback(Uri path, List<int> bytes); |
| 136 | 134 |
| 137 /// Called to write a stream into a file. | 135 /// Called to write a stream into a file. |
| 138 typedef Future WriteStreamFileCallback(Uri path, Stream<List<int>> bytes); | 136 typedef Future WriteStreamFileCallback(Uri path, Stream<List<int>> bytes); |
| 139 | 137 |
| 140 /// Called to read a file. | 138 /// Called to read a file. |
| 141 typedef Future<List<int>> ReadFileCallback(Uri path); | 139 typedef Future<List<int>> ReadFileCallback(Uri path); |
| 142 | 140 |
| 143 /// Called to list all files under some path. | 141 /// Called to list all files under some path. |
| 144 typedef Future<List<Map<String,String>>> ListFilesCallback(Uri path); | 142 typedef Future<List<Map<String, String>>> ListFilesCallback(Uri path); |
| 145 | 143 |
| 146 /// Called when we need information about the server. | 144 /// Called when we need information about the server. |
| 147 typedef Future<Uri> ServerInformationCallback(); | 145 typedef Future<Uri> ServerInformationCallback(); |
| 148 | 146 |
| 149 /// Called when we want to [enable] or disable the web server. | 147 /// Called when we want to [enable] or disable the web server. |
| 150 typedef Future<Uri> WebServerControlCallback(bool enable); | 148 typedef Future<Uri> WebServerControlCallback(bool enable); |
| 151 | 149 |
| 152 /// Hooks that are setup by the embedder. | 150 /// Hooks that are setup by the embedder. |
| 153 class VMServiceEmbedderHooks { | 151 class VMServiceEmbedderHooks { |
| 154 static ServerStartCallback serverStart; | 152 static ServerStartCallback serverStart; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 195 void _eventMessageHandler(List eventMessage) { | 193 void _eventMessageHandler(List eventMessage) { |
| 196 var streamId = eventMessage[0]; | 194 var streamId = eventMessage[0]; |
| 197 var event = eventMessage[1]; | 195 var event = eventMessage[1]; |
| 198 for (var client in clients) { | 196 for (var client in clients) { |
| 199 if (client.sendEvents && client.streams.contains(streamId)) { | 197 if (client.sendEvents && client.streams.contains(streamId)) { |
| 200 client.post(event); | 198 client.post(event); |
| 201 } | 199 } |
| 202 } | 200 } |
| 203 } | 201 } |
| 204 | 202 |
| 205 void _controlMessageHandler(int code, | 203 void _controlMessageHandler(int code, int portId, SendPort sp, String name) { |
| 206 int portId, | |
| 207 SendPort sp, | |
| 208 String name) { | |
| 209 switch (code) { | 204 switch (code) { |
| 210 case Constants.ISOLATE_STARTUP_MESSAGE_ID: | 205 case Constants.ISOLATE_STARTUP_MESSAGE_ID: |
| 211 runningIsolates.isolateStartup(portId, sp, name); | 206 runningIsolates.isolateStartup(portId, sp, name); |
| 212 break; | 207 break; |
| 213 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: | 208 case Constants.ISOLATE_SHUTDOWN_MESSAGE_ID: |
| 214 runningIsolates.isolateShutdown(portId, sp); | 209 runningIsolates.isolateShutdown(portId, sp); |
| 215 IsolateEmbedderData ied = isolateEmbedderData.remove(portId); | 210 IsolateEmbedderData ied = isolateEmbedderData.remove(portId); |
| 216 if (ied != null) { | 211 if (ied != null) { |
| 217 ied.cleanup(); | 212 ied.cleanup(); |
| 218 } | 213 } |
| 219 break; | 214 break; |
| 220 } | 215 } |
| 221 } | 216 } |
| 222 | 217 |
| 223 Future<Null> _serverMessageHandler(int code, SendPort sp, bool enable) async { | 218 Future<Null> _serverMessageHandler(int code, SendPort sp, bool enable) async { |
| 224 switch (code) { | 219 switch (code) { |
| 225 case Constants.WEB_SERVER_CONTROL_MESSAGE_ID: | 220 case Constants.WEB_SERVER_CONTROL_MESSAGE_ID: |
| 226 if (VMServiceEmbedderHooks.webServerControl == null) { | 221 if (VMServiceEmbedderHooks.webServerControl == null) { |
| 227 sp.send(null); | 222 sp.send(null); |
| 228 return; | 223 return; |
| 229 } | 224 } |
| 230 Uri uri = await VMServiceEmbedderHooks.webServerControl(enable); | 225 Uri uri = await VMServiceEmbedderHooks.webServerControl(enable); |
| 231 sp.send(uri); | 226 sp.send(uri); |
| 232 break; | 227 break; |
| 233 case Constants.SERVER_INFO_MESSAGE_ID: | 228 case Constants.SERVER_INFO_MESSAGE_ID: |
| 234 if (VMServiceEmbedderHooks.serverInformation == null) { | 229 if (VMServiceEmbedderHooks.serverInformation == null) { |
| 235 sp.send(null); | 230 sp.send(null); |
| 236 return; | 231 return; |
| 237 } | 232 } |
| 238 Uri uri = await VMServiceEmbedderHooks.serverInformation(); | 233 Uri uri = await VMServiceEmbedderHooks.serverInformation(); |
| 239 sp.send(uri); | 234 sp.send(uri); |
| 240 break; | 235 break; |
| 241 } | 236 } |
| 242 } | 237 } |
| 243 | 238 |
| 244 Future _exit() async { | 239 Future _exit() async { |
| 245 // Stop the server. | 240 // Stop the server. |
| 246 if (VMServiceEmbedderHooks.serverStop != null) { | 241 if (VMServiceEmbedderHooks.serverStop != null) { |
| 247 await VMServiceEmbedderHooks.serverStop(); | 242 await VMServiceEmbedderHooks.serverStop(); |
| 248 } | 243 } |
| 249 | 244 |
| 250 // Close receive ports. | 245 // Close receive ports. |
| (...skipping 25 matching lines...) Expand all Loading... |
| 276 } | 271 } |
| 277 if (message.length == 1) { | 272 if (message.length == 1) { |
| 278 // This is a control message directing the vm service to exit. | 273 // This is a control message directing the vm service to exit. |
| 279 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); | 274 assert(message[0] == Constants.SERVICE_EXIT_MESSAGE_ID); |
| 280 _exit(); | 275 _exit(); |
| 281 return; | 276 return; |
| 282 } | 277 } |
| 283 if (message.length == 3) { | 278 if (message.length == 3) { |
| 284 // This is a message interacting with the web server. | 279 // This is a message interacting with the web server. |
| 285 assert((message[0] == Constants.WEB_SERVER_CONTROL_MESSAGE_ID) || | 280 assert((message[0] == Constants.WEB_SERVER_CONTROL_MESSAGE_ID) || |
| 286 (message[0] == Constants.SERVER_INFO_MESSAGE_ID)); | 281 (message[0] == Constants.SERVER_INFO_MESSAGE_ID)); |
| 287 _serverMessageHandler(message[0], message[1], message[2]); | 282 _serverMessageHandler(message[0], message[1], message[2]); |
| 288 return; | 283 return; |
| 289 } | 284 } |
| 290 if (message.length == 4) { | 285 if (message.length == 4) { |
| 291 // This is a message informing us of the birth or death of an | 286 // This is a message informing us of the birth or death of an |
| 292 // isolate. | 287 // isolate. |
| 293 _controlMessageHandler(message[0], message[1], message[2], message[3]); | 288 _controlMessageHandler(message[0], message[1], message[2], message[3]); |
| 294 return; | 289 return; |
| 295 } | 290 } |
| 296 } | 291 } |
| 297 print('Internal vm-service error: ignoring illegal message: $message'); | 292 print('Internal vm-service error: ignoring illegal message: $message'); |
| 298 } | 293 } |
| 299 | 294 |
| 300 VMService._internal() | 295 VMService._internal() : eventPort = isolateControlPort { |
| 301 : eventPort = isolateControlPort { | |
| 302 eventPort.handler = messageHandler; | 296 eventPort.handler = messageHandler; |
| 303 } | 297 } |
| 304 | 298 |
| 305 factory VMService() { | 299 factory VMService() { |
| 306 if (VMService._instance == null) { | 300 if (VMService._instance == null) { |
| 307 VMService._instance = new VMService._internal(); | 301 VMService._instance = new VMService._internal(); |
| 308 _onStart(); | 302 _onStart(); |
| 309 } | 303 } |
| 310 return _instance; | 304 return _instance; |
| 311 } | 305 } |
| 312 | 306 |
| 313 bool _isAnyClientSubscribed(String streamId) { | 307 bool _isAnyClientSubscribed(String streamId) { |
| 314 for (var client in clients) { | 308 for (var client in clients) { |
| 315 if (client.streams.contains(streamId)) { | 309 if (client.streams.contains(streamId)) { |
| 316 return true; | 310 return true; |
| 317 } | 311 } |
| 318 } | 312 } |
| 319 return false; | 313 return false; |
| 320 } | 314 } |
| 321 | 315 |
| 322 Future<String> _streamListen(Message message) async { | 316 Future<String> _streamListen(Message message) async { |
| 323 var client = message.client; | 317 var client = message.client; |
| 324 var streamId = message.params['streamId']; | 318 var streamId = message.params['streamId']; |
| 325 | 319 |
| 326 if (client.streams.contains(streamId)) { | 320 if (client.streams.contains(streamId)) { |
| 327 return encodeRpcError(message, kStreamAlreadySubscribed); | 321 return encodeRpcError(message, kStreamAlreadySubscribed); |
| 328 } | 322 } |
| 329 if (!_isAnyClientSubscribed(streamId)) { | 323 if (!_isAnyClientSubscribed(streamId)) { |
| 330 if (!_vmListenStream(streamId)) { | 324 if (!_vmListenStream(streamId)) { |
| 331 return encodeRpcError( | 325 return encodeRpcError(message, kInvalidParams, |
| 332 message, kInvalidParams, | 326 details: "streamListen: invalid 'streamId' parameter: ${streamId}"); |
| 333 details:"streamListen: invalid 'streamId' parameter: ${streamId}"); | |
| 334 } | 327 } |
| 335 } | 328 } |
| 336 client.streams.add(streamId); | 329 client.streams.add(streamId); |
| 337 | 330 |
| 338 return encodeSuccess(message); | 331 return encodeSuccess(message); |
| 339 } | 332 } |
| 340 | 333 |
| 341 Future<String> _streamCancel(Message message) async { | 334 Future<String> _streamCancel(Message message) async { |
| 342 var client = message.client; | 335 var client = message.client; |
| 343 var streamId = message.params['streamId']; | 336 var streamId = message.params['streamId']; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 362 return encodeInvalidParamError(message, 'token'); | 355 return encodeInvalidParamError(message, 'token'); |
| 363 } | 356 } |
| 364 var uri = message.params['uri']; | 357 var uri = message.params['uri']; |
| 365 if (uri == null) { | 358 if (uri == null) { |
| 366 return encodeMissingParamError(message, 'uri'); | 359 return encodeMissingParamError(message, 'uri'); |
| 367 } | 360 } |
| 368 if (uri is! String) { | 361 if (uri is! String) { |
| 369 return encodeInvalidParamError(message, 'uri'); | 362 return encodeInvalidParamError(message, 'uri'); |
| 370 } | 363 } |
| 371 var args = message.params['args']; | 364 var args = message.params['args']; |
| 372 if (args != null && | 365 if (args != null && args is! List<String>) { |
| 373 args is! List<String>) { | |
| 374 return encodeInvalidParamError(message, 'args'); | 366 return encodeInvalidParamError(message, 'args'); |
| 375 } | 367 } |
| 376 var msg = message.params['message']; | 368 var msg = message.params['message']; |
| 377 | 369 |
| 378 Isolate.spawnUri(Uri.parse(uri), args, msg).then((isolate) { | 370 Isolate.spawnUri(Uri.parse(uri), args, msg).then((isolate) { |
| 379 _spawnUriNotify(isolate.controlPort, token); | 371 _spawnUriNotify(isolate.controlPort, token); |
| 380 }).catchError((e) { | 372 }).catchError((e) { |
| 381 _spawnUriNotify(e.toString(), token); | 373 _spawnUriNotify(e.toString(), token); |
| 382 }); | 374 }); |
| 383 | 375 |
| 384 return encodeSuccess(message); | 376 return encodeSuccess(message); |
| 385 } | 377 } |
| 386 | 378 |
| 387 static responseAsJson(portResponse) { | 379 static responseAsJson(portResponse) { |
| 388 if (portResponse is String) { | 380 if (portResponse is String) { |
| 389 return JSON.decode(portResponse); | 381 return JSON.decode(portResponse); |
| 390 } else { | 382 } else { |
| 391 var cstring = portResponse[0]; | 383 var cstring = portResponse[0]; |
| 392 return JSON.fuse(UTF8).decode(cstring); | 384 return JSON.fuse(UTF8).decode(cstring); |
| 393 } | 385 } |
| 394 } | 386 } |
| 395 | 387 |
| 396 // TODO(johnmccutchan): Turn this into a command line tool that uses the | 388 // TODO(johnmccutchan): Turn this into a command line tool that uses the |
| 397 // service library. | 389 // service library. |
| 398 Future<String> _getCrashDump(Message message) async { | 390 Future<String> _getCrashDump(Message message) async { |
| 399 var client = message.client; | 391 var client = message.client; |
| 400 final perIsolateRequests = [ | 392 final perIsolateRequests = [ |
| 401 // ?isolateId=<isolate id> will be appended to each of these requests. | 393 // ?isolateId=<isolate id> will be appended to each of these requests. |
| 402 // Isolate information. | 394 // Isolate information. |
| 403 Uri.parse('getIsolate'), | 395 Uri.parse('getIsolate'), |
| 404 // State of heap. | 396 // State of heap. |
| 405 Uri.parse('_getAllocationProfile'), | 397 Uri.parse('_getAllocationProfile'), |
| 406 // Call stack + local variables. | 398 // Call stack + local variables. |
| 407 Uri.parse('getStack?_full=true'), | 399 Uri.parse('getStack?_full=true'), |
| 408 ]; | 400 ]; |
| 409 | 401 |
| 410 // Snapshot of running isolates. | 402 // Snapshot of running isolates. |
| 411 var isolates = runningIsolates.isolates.values.toList(); | 403 var isolates = runningIsolates.isolates.values.toList(); |
| 412 | 404 |
| 413 // Collect the mapping from request uris to responses. | 405 // Collect the mapping from request uris to responses. |
| 414 var responses = { | 406 var responses = {}; |
| 415 }; | |
| 416 | 407 |
| 417 // Request VM. | 408 // Request VM. |
| 418 var getVM = Uri.parse('getVM'); | 409 var getVM = Uri.parse('getVM'); |
| 419 var getVmResponse = responseAsJson( | 410 var getVmResponse = |
| 420 await new Message.fromUri(client, getVM).sendToVM()); | 411 responseAsJson(await new Message.fromUri(client, getVM).sendToVM()); |
| 421 responses[getVM.toString()] = getVmResponse['result']; | 412 responses[getVM.toString()] = getVmResponse['result']; |
| 422 | 413 |
| 423 // Request command line flags. | 414 // Request command line flags. |
| 424 var getFlagList = Uri.parse('getFlagList'); | 415 var getFlagList = Uri.parse('getFlagList'); |
| 425 var getFlagListResponse = responseAsJson( | 416 var getFlagListResponse = responseAsJson( |
| 426 await new Message.fromUri(client, getFlagList).sendToVM()); | 417 await new Message.fromUri(client, getFlagList).sendToVM()); |
| 427 responses[getFlagList.toString()] = getFlagListResponse['result']; | 418 responses[getFlagList.toString()] = getFlagListResponse['result']; |
| 428 | 419 |
| 429 // Make requests to each isolate. | 420 // Make requests to each isolate. |
| 430 for (var isolate in isolates) { | 421 for (var isolate in isolates) { |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 501 external bool _vmListenStream(String streamId); | 492 external bool _vmListenStream(String streamId); |
| 502 | 493 |
| 503 /// Cancel a subscription to a service stream. | 494 /// Cancel a subscription to a service stream. |
| 504 external void _vmCancelStream(String streamId); | 495 external void _vmCancelStream(String streamId); |
| 505 | 496 |
| 506 /// Get the bytes to the tar archive. | 497 /// Get the bytes to the tar archive. |
| 507 external Uint8List _requestAssets(); | 498 external Uint8List _requestAssets(); |
| 508 | 499 |
| 509 /// Notify the vm service that an isolate has been spawned via rpc. | 500 /// Notify the vm service that an isolate has been spawned via rpc. |
| 510 external void _spawnUriNotify(obj, String token); | 501 external void _spawnUriNotify(obj, String token); |
| OLD | NEW |