blob: 4640ff5051759fdd132de539b4bb1c7983f3eeab [file] [log] [blame]
// Copyright (c) 2013, 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 '../compiler.dart' as api show Diagnostic;
import 'dart2js.dart' show AbortLeg;
import 'colors.dart' as colors;
import 'source_file.dart';
import 'filenames.dart';
import 'util/uri_extras.dart';
import 'dart:typed_data';
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);
var bytes = file.readIntoSync(buffer, 0, length);
file.closeSync();
return buffer;
}
abstract class SourceFileProvider {
bool isWindows = (Platform.operatingSystem == 'windows');
Uri cwd = currentDirectory;
Map<String, SourceFile> sourceFiles = <String, 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') {
throw new ArgumentError("Unknown scheme in uri '$resourceUri'");
}
List<int> source;
try {
source = readAll(uriPathToNative(resourceUri.path));
} on FileSystemException catch (ex) {
return new Future.error(
"Error reading '${relativize(cwd, resourceUri, isWindows)}' "
"(${ex.osError})");
}
dartCharactersRead += source.length;
sourceFiles[resourceUri.toString()] = new Utf8BytesSourceFile(
relativize(cwd, resourceUri, isWindows), source);
return new Future.value(source);
}
Future/*<List<int> | String>*/ call(Uri resourceUri);
}
class CompilerSourceFileProvider extends SourceFileProvider {
Future<List<int>> call(Uri resourceUri) => readUtf8BytesFromUri(resourceUri);
}
class FormattingDiagnosticHandler {
final SourceFileProvider provider;
bool showWarnings = true;
bool showHints = true;
bool verbose = false;
bool isAborting = false;
bool enableColors = false;
bool throwOnError = false;
api.Diagnostic lastKind = null;
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');
}
}
void diagnosticHandler(Uri uri, int begin, int end, String message,
api.Diagnostic kind) {
// TODO(ahe): Remove this when source map is handled differently.
if (identical(kind.name, 'source map')) return;
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;
}
// [previousKind]/[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.
var previousKind = lastKind;
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) {
assert(fatal);
print(color(message));
} else {
SourceFile file = provider.sourceFiles[uri.toString()];
if (file == null) {
throw '$uri: file is null';
}
print(file.getLocationMessage(color(message), begin, end, true, color));
}
if (fatal && throwOnError) {
isAborting = true;
throw new AbortLeg(message);
}
}
void call(Uri uri, int begin, int end, String message, api.Diagnostic kind) {
return diagnosticHandler(uri, begin, end, message, kind);
}
}