Index: sdk/lib/vmservice/devfs.dart |
diff --git a/sdk/lib/vmservice/devfs.dart b/sdk/lib/vmservice/devfs.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..35af74822d7b27accf8b66c60175c7cdcab29576 |
--- /dev/null |
+++ b/sdk/lib/vmservice/devfs.dart |
@@ -0,0 +1,319 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of dart._vmservice; |
+ |
+String _encodeDevFSDisabledError(Message message) { |
+ return encodeRpcError( |
+ message, kFeatureDisabled, |
+ details: "DevFS is not supported by this Dart implementation"); |
+} |
+ |
+String _encodeFileSystemAlreadyExistsError(Message message, String fsName) { |
+ return encodeRpcError( |
+ message, kFileSystemAlreadyExists, |
+ details: "${message.method}: file system '${fsName}' already exists"); |
+} |
+ |
+String _encodeFileSystemDoesNotExistError(Message message, String fsName) { |
+ return encodeRpcError( |
+ message, kFileSystemDoesNotExist, |
+ details: "${message.method}: file system '${fsName}' does not exist"); |
+} |
+ |
+class _FileSystem { |
+ _FileSystem(this.name, this.uri); |
+ |
+ final String name; |
+ final Uri uri; |
+ |
+ Uri resolvePath(String path) { |
+ if (path.startsWith('/')) { |
+ path = path.substring(1); |
+ } |
+ if (path.isEmpty) { |
+ return null; |
+ } |
+ Uri pathUri; |
+ try { |
+ pathUri = Uri.parse(path); |
+ } on FormatException catch(e) { |
+ return null; |
+ } |
+ Uri resolvedUri = uri.resolveUri(pathUri); |
+ if (!resolvedUri.toString().startsWith(uri.toString())) { |
+ // Resolved uri must be within the filesystem's base uri. |
+ return null; |
+ } |
+ return resolvedUri; |
+ } |
+ |
+ Map toMap() { |
+ return { |
+ 'type': 'FileSystem', |
+ 'name': name, |
+ 'uri': uri.toString(), |
+ }; |
+ } |
+} |
+ |
+class DevFS { |
+ DevFS(); |
+ |
+ Map<String, _FileSystem> _fsMap = {}; |
+ |
+ final Set _rpcNames = new Set.from([ |
+ '_listDevFS', |
+ '_createDevFS', |
+ '_deleteDevFS', |
+ '_readDevFSFile', |
+ '_writeDevFSFile', |
+ '_writeDevFSFiles', |
+ '_listDevFSFiles', |
+ ]); |
+ |
+ void cleanup() { |
+ var deleteDir = VMServiceEmbedderHooks.deleteDir; |
+ if (deleteDir == null) { |
+ return; |
+ } |
+ var deletions = []; |
+ for (var fs in _fsMap.values) { |
+ deletions.add(deleteDir(fs.uri)); |
+ } |
+ Future.wait(deletions); |
+ _fsMap.clear(); |
+ } |
+ |
+ bool shouldHandleMessage(Message message) { |
+ return _rpcNames.contains(message.method); |
+ } |
+ |
+ Future<String> handleMessage(Message message) async { |
+ switch (message.method) { |
+ case '_listDevFS': |
+ return _listDevFS(message); |
+ case '_createDevFS': |
+ return _createDevFS(message); |
+ case '_deleteDevFS': |
+ return _deleteDevFS(message); |
+ case '_readDevFSFile': |
+ return _readDevFSFile(message); |
+ case '_writeDevFSFile': |
+ return _writeDevFSFile(message); |
+ case '_writeDevFSFiles': |
+ return _writeDevFSFiles(message); |
+ case '_listDevFSFiles': |
+ return _listDevFSFiles(message); |
+ default: |
+ return encodeRpcError( |
+ message, kInternalError, |
+ details: 'Unexpected rpc ${message.method}'); |
+ } |
+ } |
+ |
+ Future<String> _listDevFS(Message message) async { |
+ var result = {}; |
+ result['type'] = 'FileSystemList'; |
+ result['fsNames'] = _fsMap.keys.toList(); |
+ return encodeResult(message, result); |
+ } |
+ |
+ Future<String> _createDevFS(Message message) async { |
+ var createTempDir = VMServiceEmbedderHooks.createTempDir; |
+ if (createTempDir == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap[fsName]; |
+ if (fs != null) { |
+ return _encodeFileSystemAlreadyExistsError(message, fsName); |
+ } |
+ var tempDir = await createTempDir(fsName); |
+ fs = new _FileSystem(fsName, tempDir); |
+ _fsMap[fsName] = fs; |
+ return encodeResult(message, fs.toMap()); |
+ } |
+ |
+ Future<String> _deleteDevFS(Message message) async { |
+ var deleteDir = VMServiceEmbedderHooks.deleteDir; |
+ if (deleteDir == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap.remove(fsName); |
+ if (fs == null) { |
+ return _encodeFileSystemDoesNotExistError(message, fsName); |
+ } |
+ await deleteDir(fs.uri); |
+ return encodeSuccess(message); |
+ } |
+ |
+ Future<String> _readDevFSFile(Message message) async { |
+ var readFile = VMServiceEmbedderHooks.readFile; |
+ if (readFile == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap[fsName]; |
+ if (fs == null) { |
+ return _encodeFileSystemDoesNotExistError(message, fsName); |
+ } |
+ var path = message.params['path']; |
+ if (path == null) { |
+ return encodeMissingParamError(message, 'path'); |
+ } |
+ if (path is! String) { |
+ return encodeInvalidParamError(message, 'path'); |
+ } |
+ Uri uri = fs.resolvePath(path); |
+ if (uri == null) { |
+ return encodeInvalidParamError(message, 'path'); |
+ } |
+ |
+ try { |
+ List<int> bytes = await readFile(uri); |
+ var result = { |
+ 'type': 'FSFile', |
+ 'fileContents': BASE64.encode(bytes) |
+ }; |
+ return encodeResult(message, result); |
+ } catch (e) { |
+ return encodeRpcError( |
+ message, kFileDoesNotExist, |
+ details: "_readDevFSFile: $e"); |
+ } |
+ } |
+ |
+ Future<String> _writeDevFSFile(Message message) async { |
+ var writeFile = VMServiceEmbedderHooks.writeFile; |
+ if (writeFile == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap[fsName]; |
+ if (fs == null) { |
+ return _encodeFileSystemDoesNotExistError(message, fsName); |
+ } |
+ var path = message.params['path']; |
+ if (path == null) { |
+ return encodeMissingParamError(message, 'path'); |
+ } |
+ if (path is! String) { |
+ return encodeInvalidParamError(message, 'path'); |
+ } |
+ Uri uri = fs.resolvePath(path); |
+ if (uri == null) { |
+ return encodeInvalidParamError(message, 'path'); |
+ } |
+ var fileContents = message.params['fileContents']; |
+ if (fileContents == null) { |
+ return encodeMissingParamError(message, 'fileContents'); |
+ } |
+ if (fileContents is! String) { |
+ return encodeInvalidParamError(message, 'fileContents'); |
+ } |
+ List<int> decodedFileContents = BASE64.decode(fileContents); |
+ |
+ await writeFile(uri, decodedFileContents); |
+ return encodeSuccess(message); |
+ } |
+ |
+ Future<String> _writeDevFSFiles(Message message) async { |
+ var writeFile = VMServiceEmbedderHooks.writeFile; |
+ if (writeFile == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap[fsName]; |
+ if (fs == null) { |
+ return _encodeFileSystemDoesNotExistError(message, fsName); |
+ } |
+ var files = message.params['files']; |
+ if (files == null) { |
+ return encodeMissingParamError(message, 'files'); |
+ } |
+ if (files is! List) { |
+ return encodeInvalidParamError(message, 'files'); |
+ } |
+ var uris = []; |
+ for (int i = 0; i < files.length; i++) { |
+ var fileInfo = files[i]; |
+ if (fileInfo is! List || |
+ fileInfo.length != 2 || |
+ fileInfo[0] is! String || fileInfo[1] is! String) { |
+ return encodeRpcError( |
+ message, kInvalidParams, |
+ details: "${message.method}: invalid 'files' parameter " |
+ "at index ${i}: ${fileInfo}"); |
+ } |
+ var uri = fs.resolvePath(fileInfo[0]); |
+ if (uri == null) { |
+ return encodeRpcError( |
+ message, kInvalidParams, |
+ details: "${message.method}: invalid 'files' parameter " |
+ "at index ${i}: ${fileInfo}"); |
+ } |
+ uris.add(uri); |
+ } |
+ var pendingWrites = []; |
+ for (int i = 0; i < uris.length; i++) { |
+ List<int> decodedFileContents = BASE64.decode(files[i][1]); |
+ pendingWrites.add(writeFile(uris[i], decodedFileContents)); |
+ } |
+ await Future.wait(pendingWrites); |
+ return encodeSuccess(message); |
+ } |
+ |
+ Future<String> _listDevFSFiles(Message message) async { |
+ var listFiles = VMServiceEmbedderHooks.listFiles; |
+ if (listFiles == null) { |
+ return _encodeDevFSDisabledError(message); |
+ } |
+ var fsName = message.params['fsName']; |
+ if (fsName == null) { |
+ return encodeMissingParamError(message, 'fsName'); |
+ } |
+ if (fsName is! String) { |
+ return encodeInvalidParamError(message, 'fsName'); |
+ } |
+ var fs = _fsMap[fsName]; |
+ if (fs == null) { |
+ return _encodeFileSystemDoesNotExistError(message, fsName); |
+ } |
+ var fileList = await listFiles(fs.uri); |
+ var result = { 'type': 'FSFileList', 'files': fileList }; |
+ return encodeResult(message, result); |
+ } |
+} |