Chromium Code Reviews| 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) { |
|
Cutch
2016/07/01 21:23:17
(here and elsewhere) the null check above is redun
|
| + 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); |
| + } |
| +} |