Index: pkg/dart_scanner/lib/io.dart |
diff --git a/pkg/dart_scanner/lib/io.dart b/pkg/dart_scanner/lib/io.dart |
index 94052cb6bc0f8448f4a12af6715bf4b610b448ac..29c52f6f0cef8ad3118a84557b864f6e257da699 100644 |
--- a/pkg/dart_scanner/lib/io.dart |
+++ b/pkg/dart_scanner/lib/io.dart |
@@ -1,408 +1,44 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// 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. |
- |
-library source_file_provider; |
- |
-import 'dart:async'; |
-import 'dart:convert'; |
-import 'dart:io'; |
-import 'dart:math' as math; |
-import 'dart:typed_data'; |
- |
-import '../compiler.dart' as api show Diagnostic; |
-import '../compiler_new.dart' as api; |
-import '../compiler_new.dart'; |
-import 'colors.dart' as colors; |
-import 'dart2js.dart' show AbortLeg; |
-import 'filenames.dart'; |
-import 'io/source_file.dart'; |
-import 'util/uri_extras.dart'; |
- |
-abstract class SourceFileProvider implements CompilerInput { |
- bool isWindows = (Platform.operatingSystem == 'windows'); |
- Uri cwd = currentDirectory; |
- Map<Uri, SourceFile> sourceFiles = <Uri, SourceFile>{}; |
- int dartCharactersRead = 0; |
- |
- Future<String> readStringFromUri(Uri resourceUri) { |
- return readUtf8BytesFromUri(resourceUri).then(UTF8.decode); |
- } |
- |
- Future<List<int>> readUtf8BytesFromUri(Uri resourceUri) { |
- if (resourceUri.scheme == 'file') { |
- return _readFromFile(resourceUri); |
- } else if (resourceUri.scheme == 'http' || resourceUri.scheme == 'https') { |
- return _readFromHttp(resourceUri); |
- } else { |
- throw new ArgumentError("Unknown scheme in uri '$resourceUri'"); |
- } |
- } |
- |
- Future<List<int>> _readFromFile(Uri resourceUri) { |
- assert(resourceUri.scheme == 'file'); |
- List<int> source; |
- try { |
- source = readAll(resourceUri.toFilePath()); |
- } on FileSystemException catch (ex) { |
- String message = ex.osError?.message; |
- String detail = message != null ? ' ($message)' : ''; |
- return new Future.error( |
- "Error reading '${relativizeUri(resourceUri)}' $detail"); |
- } |
- dartCharactersRead += source.length; |
- sourceFiles[resourceUri] = new CachingUtf8BytesSourceFile( |
- resourceUri, relativizeUri(resourceUri), source); |
- return new Future.value(source); |
- } |
- |
- Future<List<int>> _readFromHttp(Uri resourceUri) { |
- assert(resourceUri.scheme == 'http'); |
- HttpClient client = new HttpClient(); |
- return client |
- .getUrl(resourceUri) |
- .then((HttpClientRequest request) => request.close()) |
- .then((HttpClientResponse response) { |
- if (response.statusCode != HttpStatus.OK) { |
- String msg = 'Failure getting $resourceUri: ' |
- '${response.statusCode} ${response.reasonPhrase}'; |
- throw msg; |
- } |
- return response.toList(); |
- }).then((List<List<int>> splitContent) { |
- int totalLength = splitContent.fold(0, (int old, List list) { |
- return old + list.length; |
- }); |
- Uint8List result = new Uint8List(totalLength); |
- int offset = 0; |
- for (List<int> contentPart in splitContent) { |
- result.setRange(offset, offset + contentPart.length, contentPart); |
- offset += contentPart.length; |
- } |
- dartCharactersRead += totalLength; |
- sourceFiles[resourceUri] = new CachingUtf8BytesSourceFile( |
- resourceUri, resourceUri.toString(), result); |
- return result; |
- }); |
- } |
- |
- // TODO(johnniwinther): Remove this when no longer needed for the old compiler |
- // API. |
- Future/*<List<int> | String>*/ call(Uri resourceUri) => throw "unimplemented"; |
- |
- relativizeUri(Uri uri) => relativize(cwd, uri, isWindows); |
- |
- SourceFile getSourceFile(Uri resourceUri) { |
- return sourceFiles[resourceUri]; |
- } |
-} |
- |
-List<int> readAll(String filename) { |
- var file = (new File(filename)).openSync(); |
- var length = file.lengthSync(); |
- // +1 to have a 0 terminated list, see [Scanner]. |
- var buffer = new Uint8List(length + 1); |
- file.readIntoSync(buffer, 0, length); |
- file.closeSync(); |
- return buffer; |
-} |
- |
-class CompilerSourceFileProvider extends SourceFileProvider { |
- // TODO(johnniwinther): Remove this when no longer needed for the old compiler |
- // API. |
- Future<List<int>> call(Uri resourceUri) => readFromUri(resourceUri); |
- |
- @override |
- Future readFromUri(Uri uri) => readUtf8BytesFromUri(uri); |
-} |
- |
-class FormattingDiagnosticHandler implements CompilerDiagnostics { |
- final SourceFileProvider provider; |
- bool showWarnings = true; |
- bool showHints = true; |
- bool verbose = false; |
- bool isAborting = false; |
- bool enableColors = false; |
- bool throwOnError = false; |
- int throwOnErrorCount = 0; |
- api.Diagnostic lastKind = null; |
- int fatalCount = 0; |
- |
- final int FATAL = api.Diagnostic.CRASH.ordinal | api.Diagnostic.ERROR.ordinal; |
- final int INFO = |
- api.Diagnostic.INFO.ordinal | api.Diagnostic.VERBOSE_INFO.ordinal; |
- |
- FormattingDiagnosticHandler([SourceFileProvider provider]) |
- : this.provider = |
- (provider == null) ? new CompilerSourceFileProvider() : provider; |
- |
- void info(var message, [api.Diagnostic kind = api.Diagnostic.VERBOSE_INFO]) { |
- if (!verbose && kind == api.Diagnostic.VERBOSE_INFO) return; |
- if (enableColors) { |
- print('${colors.green("Info:")} $message'); |
- } else { |
- print('Info: $message'); |
- } |
- } |
- |
- /// Adds [kind] specific prefix to [message]. |
- String prefixMessage(String message, api.Diagnostic kind) { |
- switch (kind) { |
- case api.Diagnostic.ERROR: |
- return 'Error: $message'; |
- case api.Diagnostic.WARNING: |
- return 'Warning: $message'; |
- case api.Diagnostic.HINT: |
- return 'Hint: $message'; |
- case api.Diagnostic.CRASH: |
- return 'Internal Error: $message'; |
- case api.Diagnostic.INFO: |
- case api.Diagnostic.VERBOSE_INFO: |
- return 'Info: $message'; |
- } |
- throw 'Unexpected diagnostic kind: $kind (${kind.ordinal})'; |
- } |
- |
- @override |
- void report(var code, Uri uri, int begin, int end, String message, |
- api.Diagnostic kind) { |
- if (isAborting) return; |
- isAborting = (kind == api.Diagnostic.CRASH); |
- |
- bool fatal = (kind.ordinal & FATAL) != 0; |
- bool isInfo = (kind.ordinal & INFO) != 0; |
- if (isInfo && uri == null && kind != api.Diagnostic.INFO) { |
- info(message, kind); |
- return; |
- } |
- |
- message = prefixMessage(message, kind); |
- |
- // [lastKind] records the previous non-INFO kind we saw. |
- // This is used to suppress info about a warning when warnings are |
- // suppressed, and similar for hints. |
- if (kind != api.Diagnostic.INFO) { |
- lastKind = kind; |
- } |
- var color; |
- if (kind == api.Diagnostic.ERROR) { |
- color = colors.red; |
- } else if (kind == api.Diagnostic.WARNING) { |
- if (!showWarnings) return; |
- color = colors.magenta; |
- } else if (kind == api.Diagnostic.HINT) { |
- if (!showHints) return; |
- color = colors.cyan; |
- } else if (kind == api.Diagnostic.CRASH) { |
- color = colors.red; |
- } else if (kind == api.Diagnostic.INFO) { |
- if (lastKind == api.Diagnostic.WARNING && !showWarnings) return; |
- if (lastKind == api.Diagnostic.HINT && !showHints) return; |
- color = colors.green; |
- } else { |
- throw 'Unknown kind: $kind (${kind.ordinal})'; |
- } |
- if (!enableColors) { |
- color = (x) => x; |
- } |
- if (uri == null) { |
- print('${color(message)}'); |
- } else { |
- SourceFile file = provider.sourceFiles[uri]; |
- if (file != null) { |
- print(file.getLocationMessage(color(message), begin, end, |
- colorize: color)); |
- } else { |
- String position = end - begin > 0 ? '@$begin+${end - begin}' : ''; |
- print('${provider.relativizeUri(uri)}$position:\n' |
- '${color(message)}'); |
- } |
- } |
- if (fatal && ++fatalCount >= throwOnErrorCount && throwOnError) { |
- isAborting = true; |
- throw new AbortLeg(message); |
- } |
- } |
- |
- // TODO(johnniwinther): Remove this when no longer needed for the old compiler |
- // API. |
- void call(Uri uri, int begin, int end, String message, api.Diagnostic kind) { |
- return report(null, uri, begin, end, message, kind); |
- } |
+// BSD-style license that can be found in the LICENSE.md file. |
+ |
+import 'dart:async' show |
+ Future; |
+ |
+import 'dart:io' show |
+ File, |
+ RandomAccessFile; |
+ |
+import 'dart:typed_data' show |
+ Uint8List; |
+ |
+List<int> readBytesFromFileSync(Uri uri) { |
+ RandomAccessFile file = new File.fromUri(uri).openSync(); |
+ Uint8List list; |
+ try { |
+ int length = file.lengthSync(); |
+ // +1 to have a 0 terminated list, see [Scanner]. |
+ list = new Uint8List(length + 1); |
+ file.readIntoSync(list, 0, length); |
+ } finally { |
+ file.closeSync(); |
+ } |
+ return list; |
} |
-typedef void MessageCallback(String message); |
- |
-class RandomAccessFileOutputProvider implements CompilerOutput { |
- final Uri out; |
- final Uri sourceMapOut; |
- final Uri resolutionOutput; |
- final MessageCallback onInfo; |
- final MessageCallback onFailure; |
- |
- int totalCharactersWritten = 0; |
- List<String> allOutputFiles = new List<String>(); |
- |
- RandomAccessFileOutputProvider(this.out, this.sourceMapOut, |
- {this.onInfo, this.onFailure, this.resolutionOutput}); |
- |
- static Uri computePrecompiledUri(Uri out) { |
- String extension = 'precompiled.js'; |
- String outPath = out.path; |
- if (outPath.endsWith('.js')) { |
- outPath = outPath.substring(0, outPath.length - 3); |
- return out.resolve('$outPath.$extension'); |
- } else { |
- return out.resolve(extension); |
- } |
- } |
- |
- EventSink<String> call(String name, String extension) { |
- return createEventSink(name, extension); |
- } |
- |
- EventSink<String> createEventSink(String name, String extension) { |
- Uri uri; |
- bool isPrimaryOutput = false; |
- // TODO (johnniwinther, sigurdm): Make a better interface for |
- // output-providers. |
- if (extension == "deferred_map") { |
- uri = out.resolve(name); |
- } else if (name == '') { |
- if (extension == 'js' || extension == 'dart') { |
- isPrimaryOutput = true; |
- uri = out; |
- } else if (extension == 'precompiled.js') { |
- uri = computePrecompiledUri(out); |
- onInfo("File ($uri) is compatible with header" |
- " \"Content-Security-Policy: script-src 'self'\""); |
- } else if (extension == 'js.map' || extension == 'dart.map') { |
- uri = sourceMapOut; |
- } else if (extension == 'info.json') { |
- String outName = out.path.substring(out.path.lastIndexOf('/') + 1); |
- uri = out.resolve('$outName.$extension'); |
- } else if (extension == 'data') { |
- if (resolutionOutput == null) { |
- onFailure('Serialization target unspecified.'); |
- } |
- uri = resolutionOutput; |
- } else { |
- onFailure('Unknown extension: $extension'); |
- } |
- } else { |
- uri = out.resolve('$name.$extension'); |
- } |
- |
- if (uri.scheme != 'file') { |
- onFailure('Unhandled scheme ${uri.scheme} in $uri.'); |
- } |
- |
- RandomAccessFile output; |
- try { |
- output = new File(uri.toFilePath()).openSync(mode: FileMode.WRITE); |
- } on FileSystemException catch (e) { |
- onFailure('$e'); |
- } |
- |
- allOutputFiles.add(relativize(currentDirectory, uri, Platform.isWindows)); |
- |
- int charactersWritten = 0; |
- |
- writeStringSync(String data) { |
- // Write the data in chunks of 8kb, otherwise we risk running OOM. |
- int chunkSize = 8 * 1024; |
- |
- int offset = 0; |
- while (offset < data.length) { |
- output.writeStringSync( |
- data.substring(offset, math.min(offset + chunkSize, data.length))); |
- offset += chunkSize; |
- } |
- charactersWritten += data.length; |
- } |
- |
- onDone() { |
- output.closeSync(); |
- if (isPrimaryOutput) { |
- totalCharactersWritten += charactersWritten; |
- } |
- } |
- |
- return new _EventSinkWrapper(writeStringSync, onDone); |
- } |
-} |
- |
-class _EventSinkWrapper extends EventSink<String> { |
- var onAdd, onClose; |
- |
- _EventSinkWrapper(this.onAdd, this.onClose); |
- |
- void add(String data) => onAdd(data); |
- |
- void addError(error, [StackTrace stackTrace]) => throw error; |
- |
- void close() => onClose(); |
-} |
- |
-/// Adapter to integrate dart2js in bazel. |
-/// |
-/// To handle bazel's special layout: |
-/// |
-/// * We specify a .packages configuration file that expands packages to their |
-/// corresponding bazel location. This way there is no need to create a pub |
-/// cache prior to invoking dart2js. |
-/// |
-/// * We provide an implicit mapping that can make all urls relative to the |
-/// bazel root. |
-/// To the compiler, URIs look like: |
-/// file:///bazel-root/a/b/c.dart |
-/// |
-/// even though in the file system the file is located at: |
-/// file:///path/to/the/actual/bazel/root/a/b/c.dart |
-/// |
-/// This mapping serves two purposes: |
-/// - It makes compiler results independent of the machine layout, which |
-/// enables us to share results across bazel runs and across machines. |
-/// |
-/// - It hides the distinction between generated and source files. That way |
-/// we can use the standard package-resolution mechanism and ignore the |
-/// internals of how files are organized within bazel. |
-/// |
-/// When invoking the compiler, bazel will use `package:` and |
-/// `file:///bazel-root/` URIs to specify entrypoints. |
-/// |
-/// The mapping is specified using search paths relative to the current |
-/// directory. When this provider looks up a file, the bazel-root folder is |
-/// replaced by the first directory in the search path containing the file, if |
-/// any. For example, given the search path ".,bazel-bin/", and a URL |
-/// of the form `file:///bazel-root/a/b.dart`, this provider will check if the |
-/// file exists under "./a/b.dart", then check under "bazel-bin/a/b.dart". If |
-/// none of the paths matches, it will attempt to load the file from |
-/// `/bazel-root/a/b.dart` which will likely fail. |
-class BazelInputProvider extends SourceFileProvider { |
- final List<Uri> dirs; |
- |
- BazelInputProvider(List<String> searchPaths) |
- : dirs = searchPaths.map(_resolve).toList(); |
- |
- static _resolve(String path) => currentDirectory.resolve(path); |
- |
- @override |
- Future readFromUri(Uri uri) async { |
- var resolvedUri = uri; |
- var path = uri.path; |
- if (path.startsWith('/bazel-root')) { |
- path = path.substring('/bazel-root/'.length); |
- for (var dir in dirs) { |
- var file = dir.resolve(path); |
- if (await new File.fromUri(file).exists()) { |
- resolvedUri = file; |
- break; |
- } |
- } |
- } |
- var result = await readUtf8BytesFromUri(resolvedUri); |
- sourceFiles[uri] = sourceFiles[resolvedUri]; |
- return result; |
- } |
+Future<List<int>> readBytesFromFile(Uri uri) async { |
+ RandomAccessFile file = await new File.fromUri(uri).open(); |
+ Uint8List list; |
+ try { |
+ int length = await file.length(); |
+ // +1 to have a 0 terminated list, see [Scanner]. |
+ list = new Uint8List(length + 1); |
+ int read = await file.readInto(list); |
+ if (read != length) { |
+ throw "Error reading file: ${uri}"; |
+ } |
+ } finally { |
+ await file.close(); |
+ } |
+ return list; |
} |