blob: 084b837e921a95693d295b31a1479e3e389794e3 [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.
library fasta.errors;
import 'dart:async' show Future;
import 'dart:convert' show JSON;
import 'dart:io'
show ContentType, HttpClient, HttpClientRequest, SocketException, stderr;
import 'colors.dart' show red;
import 'messages.dart' show errorsAreFatal, deprecated_format, isVerbose;
const String defaultServerAddress = "http://127.0.0.1:59410/";
/// Tracks if there has been a crash reported through [reportCrash]. Should be
/// reset between each compilation by calling [resetCrashReporting].
bool hasCrashed = false;
/// Tracks the first source URI that has been read and is used as a fall-back
/// for [reportCrash]. Should be reset between each compilation by calling
/// [resetCrashReporting].
Uri firstSourceUri;
/// Used to report an internal error.
///
/// Internal errors should be avoided as best as possible, but are preferred
/// over assertion failures. Favor error messages that starts with "Internal
/// error: " and a short description that may help a developer debug the issue.
/// This method should be called instead of using `throw`, as this allows us to
/// ensure that there are no throws anywhere in the codebase.
dynamic deprecated_internalProblem(Object error,
[Uri uri, int charOffset = -1]) {
if (uri == null && charOffset == -1) {
throw error;
} else {
throw deprecated_format(
uri, charOffset, "Internal error: ${safeToString(error)}");
}
}
/// Used to report an error in input.
///
/// Avoid using this for reporting compile-time errors, instead use
/// `LibraryBuilder.deprecated_addCompileTimeError` for those.
///
/// An input error is any error that isn't an internal error. We use the term
/// "input error" in favor of "user error". This way, if an input error isn't
/// handled correctly, the user will never see a stack trace that says "user
/// error".
dynamic deprecated_inputError(Uri uri, int charOffset, Object error) {
if (errorsAreFatal && isVerbose) {
print(StackTrace.current);
}
throw new deprecated_InputError(uri, charOffset, error);
}
String deprecated_printUnexpected(Uri uri, int charOffset, String message) {
String formattedMessage =
deprecated_formatUnexpected(uri, charOffset, message);
if (errorsAreFatal) {
print(formattedMessage);
if (isVerbose) print(StackTrace.current);
throw new deprecated_InputError(uri, charOffset, message);
}
print(formattedMessage);
return formattedMessage;
}
String deprecated_formatUnexpected(Uri uri, int charOffset, String message) {
return deprecated_format(uri, charOffset, colorError("Error: $message"));
}
String colorError(String message) {
// TODO(ahe): Colors need to be optional. Doesn't work well in Emacs or on
// Windows.
return red(message);
}
class deprecated_InputError {
final Uri uri;
final int charOffset;
final Object error;
deprecated_InputError(this.uri, int charOffset, this.error)
: this.charOffset = charOffset ?? -1;
toString() => "deprecated_InputError: $error";
String deprecated_format() =>
deprecated_formatUnexpected(uri, charOffset, safeToString(error));
}
class Crash {
final Uri uri;
final int charOffset;
final Object error;
final StackTrace trace;
Crash(this.uri, this.charOffset, this.error, this.trace);
String toString() {
return """
Crash when compiling $uri,
at character offset $charOffset:
$error${trace == null ? '' : '\n$trace'}
""";
}
}
void resetCrashReporting() {
firstSourceUri = null;
hasCrashed = false;
}
Future reportCrash(error, StackTrace trace, [Uri uri, int charOffset]) async {
note(String note) async {
stderr.write(note);
await stderr.flush();
}
if (hasCrashed) return new Future.error(error, trace);
if (error is Crash) {
trace = error.trace ?? trace;
uri = error.uri ?? uri;
charOffset = error.charOffset ?? charOffset;
error = error.error;
}
uri ??= firstSourceUri;
hasCrashed = true;
Map<String, dynamic> data = <String, dynamic>{};
data["type"] = "crash";
data["client"] = "package:fasta";
if (uri != null) data["uri"] = "$uri";
if (charOffset != null) data["offset"] = charOffset;
data["error"] = safeToString(error);
data["trace"] = "$trace";
String json = JSON.encode(data);
HttpClient client = new HttpClient();
try {
Uri uri = Uri.parse(defaultServerAddress);
HttpClientRequest request;
try {
request = await client.postUrl(uri);
} on SocketException {
// Assume the crash logger isn't running.
await client.close(force: true);
return new Future.error(error, trace);
}
if (request != null) {
await note("\nSending crash report data");
request.persistentConnection = false;
request.bufferOutput = false;
String host = request?.connectionInfo?.remoteAddress?.host;
int port = request?.connectionInfo?.remotePort;
await note(" to $host:$port");
await request
..headers.contentType = ContentType.JSON
..write(json);
await request.close();
await note(".");
}
} catch (e, s) {
await note("\n${safeToString(e)}\n$s\n");
await note("\n\n\nFE::ERROR::$json\n\n\n");
}
await client.close(force: true);
await note("\n");
return new Future.error(error, trace);
}
String safeToString(Object object) {
try {
return "$object";
} catch (e) {
return "Error when converting ${object.runtimeType} to string.";
}
}