| 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);
|
| + }
|
| +}
|
|
|