blob: 35af74822d7b27accf8b66c60175c7cdcab29576 [file] [log] [blame]
// 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);
}
}