OLD | NEW |
| (Empty) |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library fasta.errors; | |
6 | |
7 import 'dart:async' show Future; | |
8 | |
9 import 'dart:convert' show JSON; | |
10 | |
11 import 'dart:io' | |
12 show ContentType, HttpClient, HttpClientRequest, SocketException, stderr; | |
13 | |
14 import 'colors.dart' show red; | |
15 | |
16 import 'messages.dart' show errorsAreFatal, format, isVerbose; | |
17 | |
18 const String defaultServerAddress = "http://127.0.0.1:59410/"; | |
19 | |
20 /// Tracks if there has been a crash reported through [reportCrash]. Should be | |
21 /// reset between each compilation by calling [resetCrashReporting]. | |
22 bool hasCrashed = false; | |
23 | |
24 /// Tracks the first source URI that has been read and is used as a fall-back | |
25 /// for [reportCrash]. Should be reset between each compilation by calling | |
26 /// [resetCrashReporting]. | |
27 Uri firstSourceUri; | |
28 | |
29 /// Used to report an internal error. | |
30 /// | |
31 /// Internal errors should be avoided as best as possible, but are preferred | |
32 /// over assertion failures. Favor error messages that starts with "Internal | |
33 /// error: " and a short description that may help a developer debug the issue. | |
34 /// This method should be called instead of using `throw`, as this allows us to | |
35 /// ensure that there are no throws anywhere in the codebase. | |
36 dynamic internalError(Object error, [Uri uri, int charOffset = -1]) { | |
37 if (uri == null && charOffset == -1) { | |
38 throw error; | |
39 } else { | |
40 throw format(uri, charOffset, "Internal error: ${safeToString(error)}"); | |
41 } | |
42 } | |
43 | |
44 /// Used to report an error in input. | |
45 /// | |
46 /// Avoid using this for reporting compile-time errors, instead use | |
47 /// `LibraryBuilder.addCompileTimeError` for those. | |
48 /// | |
49 /// An input error is any error that isn't an internal error. We use the term | |
50 /// "input error" in favor of "user error". This way, if an input error isn't | |
51 /// handled correctly, the user will never see a stack trace that says "user | |
52 /// error". | |
53 dynamic inputError(Uri uri, int charOffset, Object error) { | |
54 if (errorsAreFatal && isVerbose) { | |
55 print(StackTrace.current); | |
56 } | |
57 throw new InputError(uri, charOffset, error); | |
58 } | |
59 | |
60 String printUnexpected(Uri uri, int charOffset, String message) { | |
61 String formattedMessage = formatUnexpected(uri, charOffset, message); | |
62 if (errorsAreFatal) { | |
63 print(formattedMessage); | |
64 if (isVerbose) print(StackTrace.current); | |
65 throw new InputError(uri, charOffset, message); | |
66 } | |
67 print(formattedMessage); | |
68 return formattedMessage; | |
69 } | |
70 | |
71 String formatUnexpected(Uri uri, int charOffset, String message) { | |
72 return format(uri, charOffset, colorError("Error: $message")); | |
73 } | |
74 | |
75 String colorError(String message) { | |
76 // TODO(ahe): Colors need to be optional. Doesn't work well in Emacs or on | |
77 // Windows. | |
78 return red(message); | |
79 } | |
80 | |
81 class InputError { | |
82 final Uri uri; | |
83 | |
84 final int charOffset; | |
85 | |
86 final Object error; | |
87 | |
88 InputError(this.uri, int charOffset, this.error) | |
89 : this.charOffset = charOffset ?? -1; | |
90 | |
91 toString() => "InputError: $error"; | |
92 | |
93 String format() => formatUnexpected(uri, charOffset, safeToString(error)); | |
94 } | |
95 | |
96 class Crash { | |
97 final Uri uri; | |
98 | |
99 final int charOffset; | |
100 | |
101 final Object error; | |
102 | |
103 final StackTrace trace; | |
104 | |
105 Crash(this.uri, this.charOffset, this.error, this.trace); | |
106 | |
107 String toString() { | |
108 return """ | |
109 Crash when compiling $uri, | |
110 at character offset $charOffset: | |
111 $error${trace == null ? '' : '\n$trace'} | |
112 """; | |
113 } | |
114 } | |
115 | |
116 void resetCrashReporting() { | |
117 firstSourceUri = null; | |
118 hasCrashed = false; | |
119 } | |
120 | |
121 Future reportCrash(error, StackTrace trace, [Uri uri, int charOffset]) async { | |
122 note(String note) async { | |
123 stderr.write(note); | |
124 await stderr.flush(); | |
125 } | |
126 | |
127 if (hasCrashed) return new Future.error(error, trace); | |
128 if (error is Crash) { | |
129 trace = error.trace ?? trace; | |
130 uri = error.uri ?? uri; | |
131 charOffset = error.charOffset ?? charOffset; | |
132 error = error.error; | |
133 } | |
134 uri ??= firstSourceUri; | |
135 hasCrashed = true; | |
136 Map<String, dynamic> data = <String, dynamic>{}; | |
137 data["type"] = "crash"; | |
138 data["client"] = "package:fasta"; | |
139 if (uri != null) data["uri"] = "$uri"; | |
140 if (charOffset != null) data["offset"] = charOffset; | |
141 data["error"] = safeToString(error); | |
142 data["trace"] = "$trace"; | |
143 String json = JSON.encode(data); | |
144 HttpClient client = new HttpClient(); | |
145 try { | |
146 Uri uri = Uri.parse(defaultServerAddress); | |
147 HttpClientRequest request; | |
148 try { | |
149 request = await client.postUrl(uri); | |
150 } on SocketException { | |
151 // Assume the crash logger isn't running. | |
152 await client.close(force: true); | |
153 return new Future.error(error, trace); | |
154 } | |
155 if (request != null) { | |
156 await note("\nSending crash report data"); | |
157 request.persistentConnection = false; | |
158 request.bufferOutput = false; | |
159 String host = request?.connectionInfo?.remoteAddress?.host; | |
160 int port = request?.connectionInfo?.remotePort; | |
161 await note(" to $host:$port"); | |
162 await request | |
163 ..headers.contentType = ContentType.JSON | |
164 ..write(json); | |
165 await request.close(); | |
166 await note("."); | |
167 } | |
168 } catch (e, s) { | |
169 await note("\n${safeToString(e)}\n$s\n"); | |
170 await note("\n\n\nFE::ERROR::$json\n\n\n"); | |
171 } | |
172 await client.close(force: true); | |
173 await note("\n"); | |
174 return new Future.error(error, trace); | |
175 } | |
176 | |
177 String safeToString(Object object) { | |
178 try { | |
179 return "$object"; | |
180 } catch (e) { | |
181 return "Error when converting ${object.runtimeType} to string."; | |
182 } | |
183 } | |
OLD | NEW |