Use URIs rather than paths in front end API.

This carries a number of benefits:

- It allows the front end to trivially support schemes other than
  "file:" (e.g. "http:") by allowing the client to supply a FileSystem
  implementation that handles them.

- It is more consistent with the functionality of the ".packages" file
  (which allows packages to map to any kind of URI).

- It allows the "bazel root" feature to be rewritten to use a magic
  scheme rather than a magic path.  (This eliminates concerns about
  the magic path overlapping with a user's use case).  Note that this
  feature has been renamed to "multi root" since it is sufficiently
  generic to be applicable to build systems other than Bazel.

- It reduces the risk of forgetting to use the front end's FileSystem
  abstraction to access the file system, since the native file system
  interfaces do not accept URIs.

R=danrubel@google.com

Review-Url: https://codereview.chromium.org/2614063007 .
This commit is contained in:
Paul Berry
2017-01-09 11:19:38 -08:00
parent 6840abebd6
commit ba241c0c8d
15 changed files with 248 additions and 245 deletions
+32 -30
View File
@@ -18,13 +18,13 @@ typedef void ErrorHandler(CompilationError error);
///
/// Not intended to be implemented or extended by clients.
class CompilerOptions {
/// The path to the Dart SDK.
/// The URI of the root of the Dart SDK (typically a "file:" URI).
///
/// If `null`, the SDK will be searched for using
/// [Platform.resolvedExecutable] as a starting point.
///
/// This option is mutually exclusive with [sdkSummary].
String sdkPath;
Uri sdkRoot;
/// Callback to which compilation errors should be delivered.
///
@@ -32,56 +32,58 @@ class CompilerOptions {
/// type [CompilationError].
ErrorHandler onError = defaultErrorHandler;
/// Path to the ".packages" file.
/// URI of the ".packages" file (typically a "file:" URI).
///
/// If `null`, the ".packages" file will be found via the standard
/// package_config search algorithm.
///
/// If the empty string, no packages file will be used.
String packagesFilePath;
/// If the URI's path component is empty (e.g. `new Uri()`), no packages file
/// will be used.
Uri packagesFileUri;
/// Paths to the input summary files (excluding the SDK summary). These files
/// should all be linked summaries. They should also be closed, in the sense
/// that any libraries they reference should also appear in [inputSummaries]
/// or [sdkSummary].
List<String> inputSummaries = [];
/// URIs of input summary files (excluding the SDK summary; typically these
/// will be "file:" URIs). These files should all be linked summaries. They
/// should also be closed, in the sense that any libraries they reference
/// should also appear in [inputSummaries] or [sdkSummary].
List<Uri> inputSummaries = [];
/// Path to the SDK summary file.
/// URI of the SDK summary file (typically a "file:" URI).
///
/// This should be a linked summary. If `null`, the SDK summary will be
/// searched for at a default location within [sdkPath].
/// searched for at a default location within [sdkRoot].
///
/// This option is mutually exclusive with [sdkPath]. TODO(paulberry): if the
/// This option is mutually exclusive with [sdkRoot]. TODO(paulberry): if the
/// VM does not contain a pickled copy of the SDK, we might need to change
/// this.
String sdkSummary;
Uri sdkSummary;
/// URI override map.
///
/// This is a map from Uri to file path. Any URI override listed in this map
/// takes precedence over the URI resolution that would be implied by the
/// packages file (see [packagesFilePath]) and/or [bazelRoots].
/// This is a map from URIs that might appear in import/export/part statements
/// to URIs that should be used to locate the corresponding files in the
/// [fileSystem]. Any URI override listed in this map takes precedence over
/// the URI resolution that would be implied by the packages file (see
/// [packagesFileUri]) and/or [multiRoots].
///
/// If a URI is not listed in this map, then the normal URI resolution
/// algorithm will be used.
///
/// TODO(paulberry): transition analyzer and dev_compiler to use the
/// "file:///bazel-root" mechanism, and then remove this.
/// "multi-root:" mechanism, and then remove this.
@deprecated
Map<Uri, String> uriOverride = {};
Map<Uri, Uri> uriOverride = {};
/// Bazel roots.
/// Multi-roots.
///
/// Any Uri that resolves to "file:///bazel-root/$rest" will be searched for
/// at "$root/$rest" ("$root\\$rest" in Windows), where "$root" is drawn from
/// this list. If the file is not found at any of those locations, the URI
/// "file:///bazel-root/$rest" will be used directly.
/// Any Uri that resolves to "multi-root:///$rest" will be searched for
/// at "$root/$rest", where "$root" is drawn from this list.
///
/// Intended use: if the Bazel workspace is located at path "$workspace", this
/// could be set to `['$workspace', '$workspace/bazel-bin',
/// '$workspace/bazel-genfiles']`, effectively overlaying source and generated
/// files.
List<String> bazelRoots = [];
/// Intended use: if the user has a Bazel workspace located at path
/// "$workspace", this could be set to the file URIs corresponding to the
/// paths for "$workspace", "$workspace/bazel-bin",
/// and "$workspace/bazel-genfiles", effectively overlaying source and
/// generated files.
List<Uri> multiRoots = [];
/// Sets the platform bit, which determines which patch files should be
/// applied to the SDK.
@@ -99,7 +101,7 @@ class CompilerOptions {
///
/// All file system access performed by the front end goes through this
/// mechanism, with one exception: if no value is specified for
/// [packagesFilePath], the packages file is located using the actual physical
/// [packagesFileUri], the packages file is located using the actual physical
/// file system. TODO(paulberry): fix this.
FileSystem fileSystem = PhysicalFileSystem.instance;
+9 -16
View File
@@ -18,22 +18,17 @@ import 'package:path/path.dart' as path;
/// Not intended to be implemented or extended by clients.
abstract class FileSystem {
/// Returns a path context suitable for use with this [FileSystem].
///
/// TODO(paulberry): try to eliminate all usages of this. Since the
/// FileSystem API now uses URIs rather than paths, it should not be needed.
path.Context get context;
/// Returns a [FileSystemEntity] corresponding to the given [path].
///
/// Uses of `..` and `.` in path are normalized before returning (so, for
/// example, `entityForPath('./foo')` and `entityForPath('foo')` are
/// equivalent). Relative paths are also converted to absolute paths.
///
/// Does not check whether a file or folder exists at the given location.
FileSystemEntity entityForPath(String path);
/// Returns a [FileSystemEntity] corresponding to the given [uri].
///
/// Uses of `..` and `.` in the URI are normalized before returning.
///
/// If [uri] is not an absolute `file:` URI, an [Error] will be thrown.
/// If the URI scheme is not supported by this file system, an [Error] will be
/// thrown.
///
/// Does not check whether a file or folder exists at the given location.
FileSystemEntity entityForUri(Uri uri);
@@ -46,14 +41,12 @@ abstract class FileSystem {
///
/// Not intended to be implemented or extended by clients.
abstract class FileSystemEntity {
/// Returns the absolute normalized path represented by this file system
/// Returns the absolute normalized URI represented by this file system
/// entity.
///
/// Note: if the [FileSystemEntity] was created using
/// [FileSystem.entityForPath], this is not necessarily the same as the path
/// that was used to create the object, since the path might have been
/// normalized.
String get path;
/// Note: this is not necessarily the same as the URI that was passed to
/// [FileSystem.entityForUri], since the URI might have been normalized.
Uri get uri;
/// Attempts to access this file system entity as a file and read its contents
/// as raw bytes.
+15 -5
View File
@@ -59,7 +59,7 @@ Future<Program> kernelForProgram(Uri source, CompilerOptions options) async {
/// which will be read are those listed in [sources],
/// [CompilerOptions.inputSummaries], and [CompilerOptions.sdkSummary]. If a
/// source file attempts to refer to a file which is not obtainable from these
/// paths, that will result in an error, even if the file exists on the
/// URIs, that will result in an error, even if the file exists on the
/// filesystem.
///
/// When [CompilerOptions.chaseDependencies] is true, this default behavior
@@ -128,7 +128,8 @@ Future<Program> kernelForBuildUnit(
Future<DartLoader> _createLoader(CompilerOptions options,
{Repository repository, Uri entry}) async {
var kernelOptions = _convertOptions(options);
var packages = await createPackages(options.packagesFilePath,
var packages = await createPackages(
_uriToPath(options.packagesFileUri, options),
discoveryPath: entry?.path);
return new DartLoader(
repository ?? new Repository(), kernelOptions, packages);
@@ -137,11 +138,12 @@ Future<DartLoader> _createLoader(CompilerOptions options,
DartOptions _convertOptions(CompilerOptions options) {
return new DartOptions(
strongMode: options.strongMode,
sdk: options.sdkPath,
sdk: _uriToPath(options.sdkRoot, options),
// TODO(sigmund): make it possible to use summaries and still compile the
// sdk sources.
sdkSummary: options.compileSdk ? null : options.sdkSummary,
packagePath: options.packagesFilePath,
sdkSummary:
options.compileSdk ? null : _uriToPath(options.sdkSummary, options),
packagePath: _uriToPath(options.packagesFileUri, options),
declaredVariables: options.declaredVariables);
}
@@ -152,6 +154,14 @@ void _reportErrors(List errors, ErrorHandler onError) {
}
}
String _uriToPath(Uri uri, CompilerOptions options) {
if (uri == null) return null;
if (uri.scheme != 'file') {
throw new StateError('Only file URIs are supported');
}
return options.fileSystem.context.fromUri(uri);
}
// TODO(sigmund): delete this class. Dartk should not format errors itself, we
// should just pass them along.
class _DartkError implements CompilationError {
+23 -20
View File
@@ -20,26 +20,29 @@ class MemoryFileSystem implements FileSystem {
@override
final p.Context context;
final Map<String, Uint8List> _files = {};
final Map<Uri, Uint8List> _files = {};
/// The "current directory" in the in-memory virtual file system.
///
/// This is used to convert relative paths to absolute paths.
String currentDirectory;
/// This is used to convert relative URIs to absolute URIs.
///
/// Always ends in a trailing '/'.
Uri currentDirectory;
MemoryFileSystem(this.context, this.currentDirectory);
@override
MemoryFileSystemEntity entityForPath(String path) =>
new MemoryFileSystemEntity._(
this, context.normalize(context.join(currentDirectory, path)));
MemoryFileSystem(this.context, Uri currentDirectory)
: currentDirectory = _addTrailingSlash(currentDirectory);
@override
MemoryFileSystemEntity entityForUri(Uri uri) {
if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
// Note: we don't have to verify that the URI's path is absolute, because
// URIs with non-empty schemes always have absolute paths.
return entityForPath(context.fromUri(uri));
return new MemoryFileSystemEntity._(
this, currentDirectory.resolveUri(uri).normalizePath());
}
static Uri _addTrailingSlash(Uri uri) {
if (!uri.path.endsWith('/')) {
uri = uri.replace(path: uri.path + '/');
}
return uri;
}
}
@@ -49,22 +52,22 @@ class MemoryFileSystemEntity implements FileSystemEntity {
final MemoryFileSystem _fileSystem;
@override
final String path;
final Uri uri;
MemoryFileSystemEntity._(this._fileSystem, this.path);
MemoryFileSystemEntity._(this._fileSystem, this.uri);
@override
int get hashCode => path.hashCode;
int get hashCode => uri.hashCode;
@override
bool operator ==(Object other) =>
other is MemoryFileSystemEntity &&
other.path == path &&
other.uri == uri &&
identical(other._fileSystem, _fileSystem);
@override
Future<List<int>> readAsBytes() async {
List<int> contents = _fileSystem._files[path];
List<int> contents = _fileSystem._files[uri];
if (contents != null) {
return contents.toList();
}
@@ -82,7 +85,7 @@ class MemoryFileSystemEntity implements FileSystemEntity {
/// If no file exists, one is created. If a file exists already, it is
/// overwritten.
void writeAsBytesSync(List<int> bytes) {
_fileSystem._files[path] = new Uint8List.fromList(bytes);
_fileSystem._files[uri] = new Uint8List.fromList(bytes);
}
/// Writes the given string to this file system entity.
@@ -95,6 +98,6 @@ class MemoryFileSystemEntity implements FileSystemEntity {
// Note: the return type of UTF8.encode is List<int>, but in practice it
// always returns Uint8List. We rely on that for efficiency, so that we
// don't have to make an extra copy.
_fileSystem._files[path] = UTF8.encode(s) as Uint8List;
_fileSystem._files[uri] = UTF8.encode(s) as Uint8List;
}
}
+15 -13
View File
@@ -23,37 +23,39 @@ class PhysicalFileSystem implements FileSystem {
@override
p.Context get context => p.context;
@override
FileSystemEntity entityForPath(String path) =>
new _PhysicalFileSystemEntity(context.normalize(context.absolute(path)));
@override
FileSystemEntity entityForUri(Uri uri) {
if (uri.scheme != 'file') throw new ArgumentError('File URI expected');
if (uri.scheme != 'file' && uri.scheme != '') {
throw new ArgumentError('File URI expected');
}
// Note: we don't have to verify that the URI's path is absolute, because
// URIs with non-empty schemes always have absolute paths.
return entityForPath(context.fromUri(uri));
var path = context.fromUri(uri);
return new _PhysicalFileSystemEntity(
context.normalize(context.absolute(path)));
}
}
/// Concrete implementation of [FileSystemEntity] for use by
/// [PhysicalFileSystem].
class _PhysicalFileSystemEntity implements FileSystemEntity {
@override
final String path;
final String _path;
_PhysicalFileSystemEntity(this.path);
_PhysicalFileSystemEntity(this._path);
@override
int get hashCode => path.hashCode;
int get hashCode => _path.hashCode;
@override
Uri get uri => p.toUri(_path);
@override
bool operator ==(Object other) =>
other is _PhysicalFileSystemEntity && other.path == path;
other is _PhysicalFileSystemEntity && other._path == _path;
@override
Future<List<int>> readAsBytes() => new io.File(path).readAsBytes();
Future<List<int>> readAsBytes() => new io.File(_path).readAsBytes();
@override
Future<String> readAsString() => new io.File(path).readAsString();
Future<String> readAsString() => new io.File(_path).readAsString();
}
@@ -39,11 +39,11 @@ class ProcessedOptions {
/// Get the [FileSystem] which should be used by the front end to access
/// files.
///
/// If the client supplied bazel roots using [CompilerOptions.bazelRoots], the
/// If the client supplied roots using [CompilerOptions.multiRoots], the
/// returned [FileSystem] will automatically perform the appropriate mapping.
FileSystem get fileSystem {
// TODO(paulberry): support bazelRoots.
assert(_raw.bazelRoots.isEmpty);
// TODO(paulberry): support multiRoots.
assert(_raw.multiRoots.isEmpty);
return _raw.fileSystem;
}
@@ -56,8 +56,7 @@ class ProcessedOptions {
await _getPackages();
var sdkLibraries =
<String, Uri>{}; // TODO(paulberry): support SDK libraries
_uriResolver =
new UriResolver(_packages, sdkLibraries, fileSystem.context);
_uriResolver = new UriResolver(_packages, sdkLibraries);
}
return _uriResolver;
}
@@ -68,15 +67,14 @@ class ProcessedOptions {
/// required to locate/read the packages file.
Future<Map<String, Uri>> _getPackages() async {
if (_packages == null) {
if (_raw.packagesFilePath == null) {
if (_raw.packagesFileUri == null) {
throw new UnimplementedError(); // TODO(paulberry): search for .packages
} else if (_raw.packagesFilePath.isEmpty) {
} else if (_raw.packagesFileUri.path.isEmpty) {
_packages = {};
} else {
var contents =
await fileSystem.entityForPath(_raw.packagesFilePath).readAsBytes();
var baseLocation = fileSystem.context.toUri(_raw.packagesFilePath);
_packages = package_config.parse(contents, baseLocation);
await fileSystem.entityForUri(_raw.packagesFileUri).readAsBytes();
_packages = package_config.parse(contents, _raw.packagesFileUri);
}
}
return _packages;
+27 -34
View File
@@ -2,13 +2,15 @@
// 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.
import 'package:path/path.dart' as p;
/// The class `UriResolver` implements the rules for resolving URIs to file
/// paths.
///
/// TODO(paulberry): Is it necessary to support the "http" scheme?
/// The class `UriResolver` implements the rules for resolving "dart:" and
/// "package:" URIs.
class UriResolver {
/// The URI scheme used for "package" URIs.
static const PACKAGE_SCHEME = 'package';
/// The URI scheme used for "dart" URIs.
static const DART_SCHEME = 'dart';
/// A map from package name to the file URI of the "lib" directory of the
/// corresponding package. This is equivalent to the format returned by
/// the "package_config" package's parse() function.
@@ -18,36 +20,27 @@ class UriResolver {
/// of the defining compilation unit of the SDK library.
final Map<String, Uri> sdkLibraries;
/// The path context which should be used to convert from file URIs to file
/// paths.
final p.Context pathContext;
UriResolver(this.packages, this.sdkLibraries);
/// The URI scheme used for "package" URIs.
static const PACKAGE_SCHEME = 'package';
/// The URI scheme used for "dart" URIs.
static const DART_SCHEME = 'dart';
/// The URI scheme used for "file" URIs.
static const FILE_SCHEME = 'file';
UriResolver(this.packages, this.sdkLibraries, this.pathContext);
/// Converts a URI to a file path.
/// Converts "package:" and "dart:" URIs to the locations of the corresponding
/// files.
///
/// If the given URI is valid, and of a recognized form, returns the file path
/// it corresponds to. Otherwise returns `null`. It is not necessary for the
/// URI to be absolute (relative URIs will be converted to relative file
/// paths).
/// If the given URI is a "package:" or "dart:" URI, is well formed, and names
/// a package or dart library that is recognized, returns the URI it resolves
/// to. If the given URI is a "package:" or "dart:" URI, and is ill-formed
/// or names a package or dart library that is not recognized, returns `null`.
///
/// Note that no I/O is performed; the file path that is returned will be
/// If the given URI has any scheme other than "package:" or "dart:", it is
/// returned unchanged.
///
/// It is not necessary for the URI to be absolute (relative URIs will be
/// passed through unchanged).
///
/// Note that no I/O is performed; the URI that is returned will be
/// independent of whether or not any particular file exists on the file
/// system.
String resolve(Uri uri) {
Uri fileUri;
if (uri.scheme == FILE_SCHEME) {
fileUri = uri;
} else {
Uri resolve(Uri uri) {
if (uri.scheme == DART_SCHEME || uri.scheme == PACKAGE_SCHEME) {
var path = uri.path;
var slashIndex = path.indexOf('/');
String prefix;
@@ -67,9 +60,9 @@ class UriResolver {
libUri = sdkLibraries[prefix];
}
if (libUri == null) return null;
fileUri = libUri.resolve(rest);
if (fileUri.scheme != FILE_SCHEME) return null;
return libUri.resolve(rest);
} else {
return uri;
}
return pathContext.fromUri(fileUri);
}
}
@@ -99,13 +99,14 @@ class _WalkerNode extends Node<_WalkerNode> {
Future<List<_WalkerNode>> computeDependencies() async {
var dependencies = <_WalkerNode>[];
// TODO(paulberry): add error recovery if the file can't be read.
var path = walker.uriResolver.resolve(uri);
if (path == null) {
var resolvedUri = walker.uriResolver.resolve(uri);
if (resolvedUri == null) {
// TODO(paulberry): If an error reporter was provided, report the error
// in the proper way and continue.
throw new StateError('Invalid URI: $uri');
}
var contents = await walker.fileSystem.entityForPath(path).readAsString();
var contents =
await walker.fileSystem.entityForUri(resolvedUri).readAsString();
var scanner = new _Scanner(contents);
var token = scanner.tokenize();
// TODO(paulberry): report errors.
@@ -35,15 +35,17 @@ class DependencyGrapherTest {
// If no starting points given, assume the first entry in [contents] is the
// single starting point.
startingPoints ??= [contents.keys.first];
var fileSystem = new MemoryFileSystem(pathos.posix, '/');
var fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///'));
contents.forEach((path, text) {
fileSystem.entityForPath(path).writeAsStringSync(text);
fileSystem.entityForUri(pathos.posix.toUri(path)).writeAsStringSync(text);
});
// TODO(paulberry): implement and test other option possibilities.
var options = new CompilerOptions()
..fileSystem = fileSystem
..chaseDependencies = true
..packagesFilePath = packagesFilePath;
..packagesFileUri = packagesFilePath == ''
? new Uri()
: pathos.posix.toUri(packagesFilePath);
var graph = await graphForProgram(
startingPoints.map(pathos.posix.toUri).toList(), options);
return graph.topologicallySortedCycles;
+48 -40
View File
@@ -30,26 +30,23 @@ class FileTest extends _BaseTestNative {
setUp() {
super.setUp();
path = join(tempPath, 'file.txt');
file = fileSystem.entityForPath(path);
file = entityForPath(path);
}
test_equals_differentPaths() {
expect(
file == fileSystem.entityForPath(join(tempPath, 'file2.txt')), isFalse);
expect(file == entityForPath(join(tempPath, 'file2.txt')), isFalse);
}
test_equals_samePath() {
expect(
file == fileSystem.entityForPath(join(tempPath, 'file.txt')), isTrue);
expect(file == entityForPath(join(tempPath, 'file.txt')), isTrue);
}
test_hashCode_samePath() {
expect(file.hashCode,
fileSystem.entityForPath(join(tempPath, 'file.txt')).hashCode);
expect(file.hashCode, entityForPath(join(tempPath, 'file.txt')).hashCode);
}
test_path() {
expect(file.path, path);
expect(file.uri, fileSystem.context.toUri(path));
}
test_readAsBytes_badUtf8() async {
@@ -128,40 +125,44 @@ abstract class MemoryFileSystemTestMixin extends _BaseTest {
tempUri = fileSystem.context.toUri(tempPath);
}
test_entityForPath() {
var path = join(tempPath, 'file.txt');
expect(fileSystem.entityForPath(path).path, path);
test_currentDirectory_trailingSlash() {
// The currentDirectory should already end in a trailing slash.
expect(fileSystem.currentDirectory.path, endsWith('/'));
// A trailing slash should automatically be appended when creating a
// MemoryFileSystem.
var path = fileSystem.currentDirectory.path;
var currentDirectoryWithoutSlash = fileSystem.currentDirectory
.replace(path: path.substring(0, path.length - 1));
expect(
new MemoryFileSystem(fileSystem.context, currentDirectoryWithoutSlash)
.currentDirectory,
fileSystem.currentDirectory);
// If the currentDirectory supplied to the MemoryFileSystem constructor
// already has a trailing slash, no further trailing slash should be added.
expect(
new MemoryFileSystem(fileSystem.context, fileSystem.currentDirectory)
.currentDirectory,
fileSystem.currentDirectory);
}
test_entityForPath_absolutize() {
expect(fileSystem.entityForPath('file.txt').path,
join(fileSystem.currentDirectory, 'file.txt'));
expect(entityForPath('file.txt').uri,
fileSystem.currentDirectory.resolve('file.txt'));
}
test_entityForPath_normalize_dot() {
expect(fileSystem.entityForPath(join(tempPath, '.', 'file.txt')).path,
join(tempPath, 'file.txt'));
expect(entityForPath(join(tempPath, '.', 'file.txt')).uri,
Uri.parse('$tempUri/file.txt'));
}
test_entityForPath_normalize_dotDot() {
expect(
fileSystem.entityForPath(join(tempPath, 'foo', '..', 'file.txt')).path,
join(tempPath, 'file.txt'));
expect(entityForPath(join(tempPath, 'foo', '..', 'file.txt')).uri,
Uri.parse('$tempUri/file.txt'));
}
test_entityForUri() {
expect(fileSystem.entityForUri(Uri.parse('$tempUri/file.txt')).path,
join(tempPath, 'file.txt'));
}
test_entityForUri_bareUri_absolute() {
expect(() => fileSystem.entityForUri(Uri.parse('/file.txt')),
throwsA(new isInstanceOf<Error>()));
}
test_entityForUri_bareUri_relative() {
expect(() => fileSystem.entityForUri(Uri.parse('file.txt')),
throwsA(new isInstanceOf<Error>()));
expect(fileSystem.entityForUri(Uri.parse('$tempUri/file.txt')).uri,
Uri.parse('$tempUri/file.txt'));
}
test_entityForUri_fileUri_relative() {
@@ -184,18 +185,18 @@ abstract class MemoryFileSystemTestMixin extends _BaseTest {
}
test_entityForUri_nonFileUri() {
expect(() => fileSystem.entityForUri(Uri.parse('package:foo/bar.dart')),
throwsA(new isInstanceOf<Error>()));
var uri = Uri.parse('package:foo/bar.dart');
expect(fileSystem.entityForUri(uri).uri, uri);
}
test_entityForUri_normalize_dot() {
expect(fileSystem.entityForUri(Uri.parse('$tempUri/./file.txt')).path,
join(tempPath, 'file.txt'));
expect(fileSystem.entityForUri(Uri.parse('$tempUri/./file.txt')).uri,
Uri.parse('$tempUri/file.txt'));
}
test_entityForUri_normalize_dotDot() {
expect(fileSystem.entityForUri(Uri.parse('$tempUri/foo/../file.txt')).path,
join(tempPath, 'file.txt'));
expect(fileSystem.entityForUri(Uri.parse('$tempUri/foo/../file.txt')).uri,
Uri.parse('$tempUri/file.txt'));
}
}
@@ -213,8 +214,14 @@ class MemoryFileSystemTestWindows extends _BaseTestWindows
abstract class _BaseTest {
MemoryFileSystem get fileSystem;
String get tempPath;
MemoryFileSystemEntity entityForPath(String path) =>
fileSystem.entityForUri(fileSystem.context.toUri(path));
String join(String path1, String path2, [String path3, String path4]);
void setUp();
}
@@ -227,8 +234,8 @@ class _BaseTestNative extends _BaseTest {
setUp() {
tempPath = pathos.join(io.Directory.systemTemp.path, 'test_file_system');
fileSystem =
new MemoryFileSystem(pathos.context, io.Directory.current.path);
fileSystem = new MemoryFileSystem(
pathos.context, pathos.toUri(io.Directory.current.path));
}
}
@@ -241,7 +248,7 @@ class _BaseTestPosix extends _BaseTest {
void setUp() {
tempPath = '/test_file_system';
fileSystem = new MemoryFileSystem(pathos.posix, '/cwd');
fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///cwd'));
}
}
@@ -254,6 +261,7 @@ class _BaseTestWindows extends _BaseTest {
void setUp() {
tempPath = r'c:\test_file_system';
fileSystem = new MemoryFileSystem(pathos.windows, r'c:\cwd');
fileSystem =
new MemoryFileSystem(pathos.windows, Uri.parse('file:///c:/cwd'));
}
}
@@ -30,35 +30,19 @@ class FileTest extends _BaseTest {
setUp() {
super.setUp();
path = p.join(tempPath, 'file.txt');
file = PhysicalFileSystem.instance.entityForPath(path);
file = PhysicalFileSystem.instance.entityForUri(p.toUri(path));
}
test_equals_differentPaths() {
expect(
file ==
PhysicalFileSystem.instance
.entityForPath(p.join(tempPath, 'file2.txt')),
isFalse);
expect(file == entityForPath(p.join(tempPath, 'file2.txt')), isFalse);
}
test_equals_samePath() {
expect(
file ==
PhysicalFileSystem.instance
.entityForPath(p.join(tempPath, 'file.txt')),
isTrue);
expect(file == entityForPath(p.join(tempPath, 'file.txt')), isTrue);
}
test_hashCode_samePath() {
expect(
file.hashCode,
PhysicalFileSystem.instance
.entityForPath(p.join(tempPath, 'file.txt'))
.hashCode);
}
test_path() {
expect(file.path, path);
expect(file.hashCode, entityForPath(p.join(tempPath, 'file.txt')).hashCode);
}
test_readAsBytes_badUtf8() async {
@@ -98,6 +82,10 @@ class FileTest extends _BaseTest {
new io.File(path).writeAsBytesSync(bytes);
expect(await file.readAsString(), '\u20ac');
}
test_uri() {
expect(file.uri, p.toUri(path));
}
}
@reflectiveTest
@@ -111,48 +99,35 @@ class PhysicalFileSystemTest extends _BaseTest {
test_entityForPath() {
var path = p.join(tempPath, 'file.txt');
expect(PhysicalFileSystem.instance.entityForPath(path).path, path);
expect(entityForPath(path).uri, p.toUri(path));
}
test_entityForPath_absolutize() {
expect(PhysicalFileSystem.instance.entityForPath('file.txt').path,
new io.File('file.txt').absolute.path);
expect(entityForPath('file.txt').uri,
p.toUri(new io.File('file.txt').absolute.path));
}
test_entityForPath_normalize_dot() {
expect(
PhysicalFileSystem.instance
.entityForPath(p.join(tempPath, '.', 'file.txt'))
.path,
p.join(tempPath, 'file.txt'));
expect(entityForPath(p.join(tempPath, '.', 'file.txt')).uri,
p.toUri(p.join(tempPath, 'file.txt')));
}
test_entityForPath_normalize_dotDot() {
expect(
PhysicalFileSystem.instance
.entityForPath(p.join(tempPath, 'foo', '..', 'file.txt'))
.path,
p.join(tempPath, 'file.txt'));
expect(entityForPath(p.join(tempPath, 'foo', '..', 'file.txt')).uri,
p.toUri(p.join(tempPath, 'file.txt')));
}
test_entityForUri() {
expect(
PhysicalFileSystem.instance
.entityForUri(Uri.parse('$tempUri/file.txt'))
.path,
p.join(tempPath, 'file.txt'));
.uri,
p.toUri(p.join(tempPath, 'file.txt')));
}
test_entityForUri_bareUri_absolute() {
expect(
() => PhysicalFileSystem.instance.entityForUri(Uri.parse('/file.txt')),
throwsA(new isInstanceOf<Error>()));
}
test_entityForUri_bareUri_relative() {
expect(
() => PhysicalFileSystem.instance.entityForUri(Uri.parse('file.txt')),
throwsA(new isInstanceOf<Error>()));
expect(PhysicalFileSystem.instance.entityForUri(Uri.parse('/file.txt')).uri,
p.toUri(p.fromUri('/file.txt')));
}
test_entityForUri_fileUri_relative() {
@@ -185,16 +160,16 @@ class PhysicalFileSystemTest extends _BaseTest {
expect(
PhysicalFileSystem.instance
.entityForUri(Uri.parse('$tempUri/./file.txt'))
.path,
p.join(tempPath, 'file.txt'));
.uri,
p.toUri(p.join(tempPath, 'file.txt')));
}
test_entityForUri_normalize_dotDot() {
expect(
PhysicalFileSystem.instance
.entityForUri(Uri.parse('$tempUri/foo/../file.txt'))
.path,
p.join(tempPath, 'file.txt'));
.uri,
p.toUri(p.join(tempPath, 'file.txt')));
}
}
@@ -202,6 +177,9 @@ class _BaseTest {
io.Directory tempDirectory;
String tempPath;
FileSystemEntity entityForPath(String path) =>
PhysicalFileSystem.instance.entityForUri(p.toUri(path));
setUp() {
tempDirectory = io.Directory.systemTemp.createTempSync('test_file_system');
tempPath = tempDirectory.absolute.path;
@@ -17,7 +17,7 @@ main() {
@reflectiveTest
class ProcessedOptionsTest {
final fileSystem = new MemoryFileSystem(pathos.posix, '/');
final fileSystem = new MemoryFileSystem(pathos.posix, Uri.parse('file:///'));
test_compileSdk_false() {
for (var value in [false, true]) {
@@ -37,46 +37,49 @@ class ProcessedOptionsTest {
test_getUriResolver_explicitPackagesFile() async {
// This .packages file should be ignored.
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForPath('/explicit.packages')
.entityForUri(Uri.parse('file:///explicit.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFilePath = '/explicit.packages';
..packagesFileUri = Uri.parse('file:///explicit.packages');
var processed = new ProcessedOptions(raw);
var uriResolver = await processed.getUriResolver();
expect(uriResolver.packages, {'foo': Uri.parse('file:///baz/')});
expect(uriResolver.pathContext, same(fileSystem.context));
}
test_getUriResolver_explicitPackagesFile_withBaseLocation() async {
// This .packages file should be ignored.
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForPath('/base/location/explicit.packages')
.entityForUri(Uri.parse('file:///base/location/explicit.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFilePath = '/base/location/explicit.packages';
..packagesFileUri = Uri.parse('file:///base/location/explicit.packages');
var processed = new ProcessedOptions(raw);
var uriResolver = await processed.getUriResolver();
expect(
uriResolver.packages, {'foo': Uri.parse('file:///base/location/baz/')});
expect(uriResolver.pathContext, same(fileSystem.context));
}
test_getUriResolver_noPackages() async {
// .packages file should be ignored.
fileSystem.entityForPath('/.packages').writeAsStringSync('foo:bar\n');
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFilePath = '';
..packagesFileUri = new Uri();
var processed = new ProcessedOptions(raw);
var uriResolver = await processed.getUriResolver();
expect(uriResolver.packages, isEmpty);
expect(uriResolver.pathContext, same(fileSystem.context));
}
}
@@ -21,7 +21,7 @@ abstract class UriResolverTest {
p.Context get pathContext;
void test_badScheme() {
_expectResolution('foo:bar/baz.dart', null);
_expectResolutionUri('foo:bar/baz.dart', Uri.parse('foo:bar/baz.dart'));
}
void test_dart() {
@@ -54,11 +54,11 @@ abstract class UriResolverTest {
}
void test_noSchemeAbsolute() {
_expectResolution('/foo.dart', null);
_expectResolutionUri('/foo.dart', Uri.parse('/foo.dart'));
}
void test_noSchemeRelative() {
_expectResolution('foo.dart', null);
_expectResolution('foo.dart', 'foo.dart');
}
void test_package() {
@@ -102,6 +102,13 @@ abstract class UriResolverTest {
/// Verifies that the resolution of [uriString] produces the path
/// [expectedResult].
void _expectResolution(String uriString, String expectedResult) {
_expectResolutionUri(uriString,
expectedResult == null ? null : pathContext.toUri(expectedResult));
}
/// Verifies that the resolution of [uriString] produces the URI
/// [expectedResult].
void _expectResolutionUri(String uriString, Uri expectedResult) {
var packages = {
'foo': _u('packages/foo/lib/'),
'bar': _u('packages/bar/lib/')
@@ -110,7 +117,7 @@ abstract class UriResolverTest {
'core': _u('sdk/lib/core/core.dart'),
'async': _u('sdk/lib/async/async.dart')
};
var uriResolver = new UriResolver(packages, sdkLibraries, pathContext);
var uriResolver = new UriResolver(packages, sdkLibraries);
expect(uriResolver.resolve(Uri.parse(uriString)), expectedResult);
}
+6 -4
View File
@@ -11,17 +11,19 @@ Future dumpToSink(Program program, StreamSink<List<int>> sink) {
}
Future kernelToSink(Uri entry, StreamSink<List<int>> sink) async {
var program = await kernelForProgram(entry,
var program = await kernelForProgram(
entry,
new CompilerOptions()
..sdkPath = 'sdk'
..packagesFilePath = '.packages'
..sdkRoot = new Uri.file('sdk')
..packagesFileUri = new Uri.file('.packages')
..onError = (e) => print(e.message));
await dumpToSink(program, sink);
}
main(args) async {
kernelToSink(Uri.base.resolve(args[0]),
kernelToSink(
Uri.base.resolve(args[0]),
// TODO(sigmund,hausner): define memory type where to dump binary data.
new StreamController<List<int>>.broadcast().sink);
}
+4 -3
View File
@@ -115,15 +115,16 @@ Future<Program> generateKernel(Uri entryUri,
var options = new CompilerOptions()
..strongMode = false
..compileSdk = compileSdk
..packagesFilePath = '.packages'
..packagesFileUri = new Uri.file('.packages')
..onError = ((e) => print('${e.message}'));
if (useSdkSummary) {
// TODO(sigmund): adjust path based on the benchmark runner architecture.
// Possibly let the runner make the file available at an architecture
// independent location.
options.sdkSummary = 'out/ReleaseX64/dart-sdk/lib/_internal/spec.sum';
options.sdkSummary =
new Uri.file('out/ReleaseX64/dart-sdk/lib/_internal/spec.sum');
} else {
options.sdkPath = 'sdk';
options.sdkRoot = new Uri.file('sdk');
}
Program program = await kernelForProgram(entryUri, options);
dartkTimer.stop();