Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(224)

Side by Side Diff: lib/src/frame.dart

Issue 1283113002: Don't throw for unparseable frames. (Closed) Base URL: git@github.com:dart-lang/stack_trace@master
Patch Set: Code review changes Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/trace.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library frame; 5 library frame;
6 6
7 import 'package:path/path.dart' as path; 7 import 'package:path/path.dart' as path;
8 8
9 import 'trace.dart'; 9 import 'trace.dart';
10 import 'unparsed_frame.dart';
10 11
11 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart:42:21) 12 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)
12 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart:42) 13 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart:42)
13 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart) 14 // #1 Foo._bar (file:///home/nweiz/code/stuff.dart)
14 final _vmFrame = new RegExp(r'^#\d+\s+(\S.*) \((.+?)((?::\d+){0,2})\)$'); 15 final _vmFrame = new RegExp(r'^#\d+\s+(\S.*) \((.+?)((?::\d+){0,2})\)$');
15 16
16 // at Object.stringify (native) 17 // at Object.stringify (native)
17 // at VW.call$0 (http://pub.dartlang.org/stuff.dart.js:560:28) 18 // at VW.call$0 (http://pub.dartlang.org/stuff.dart.js:560:28)
18 // at VW.call$0 (eval as fn 19 // at VW.call$0 (eval as fn
19 // (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28) 20 // (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28)
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 factory Frame.caller([int level=1]) { 120 factory Frame.caller([int level=1]) {
120 if (level < 0) { 121 if (level < 0) {
121 throw new ArgumentError("Argument [level] must be greater than or equal " 122 throw new ArgumentError("Argument [level] must be greater than or equal "
122 "to 0."); 123 "to 0.");
123 } 124 }
124 125
125 return new Trace.current(level + 1).frames.first; 126 return new Trace.current(level + 1).frames.first;
126 } 127 }
127 128
128 /// Parses a string representation of a Dart VM stack frame. 129 /// Parses a string representation of a Dart VM stack frame.
129 factory Frame.parseVM(String frame) { 130 factory Frame.parseVM(String frame) => _catchFormatException(frame, () {
130 // The VM sometimes folds multiple stack frames together and replaces them 131 // The VM sometimes folds multiple stack frames together and replaces them
131 // with "...". 132 // with "...".
132 if (frame == '...') { 133 if (frame == '...') {
133 return new Frame(new Uri(), null, null, '...'); 134 return new Frame(new Uri(), null, null, '...');
134 } 135 }
135 136
136 var match = _vmFrame.firstMatch(frame); 137 var match = _vmFrame.firstMatch(frame);
137 if (match == null) { 138 if (match == null) return new UnparsedFrame(frame);
138 throw new FormatException("Couldn't parse VM stack trace line '$frame'.");
139 }
140 139
141 // Get the pieces out of the regexp match. Function, URI and line should 140 // Get the pieces out of the regexp match. Function, URI and line should
142 // always be found. The column is optional. 141 // always be found. The column is optional.
143 var member = match[1] 142 var member = match[1]
144 .replaceAll(_asyncBody, "<async>") 143 .replaceAll(_asyncBody, "<async>")
145 .replaceAll("<anonymous closure>", "<fn>"); 144 .replaceAll("<anonymous closure>", "<fn>");
146 var uri = Uri.parse(match[2]); 145 var uri = Uri.parse(match[2]);
147 146
148 var lineAndColumn = match[3].split(':'); 147 var lineAndColumn = match[3].split(':');
149 var line = lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null; 148 var line = lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null;
150 var column = lineAndColumn.length > 2 ? int.parse(lineAndColumn[2]) : null; 149 var column = lineAndColumn.length > 2 ? int.parse(lineAndColumn[2]) : null;
151 return new Frame(uri, line, column, member); 150 return new Frame(uri, line, column, member);
152 } 151 });
153 152
154 /// Parses a string representation of a Chrome/V8 stack frame. 153 /// Parses a string representation of a Chrome/V8 stack frame.
155 factory Frame.parseV8(String frame) { 154 factory Frame.parseV8(String frame) => _catchFormatException(frame, () {
156 var match = _v8Frame.firstMatch(frame); 155 var match = _v8Frame.firstMatch(frame);
157 if (match == null) { 156 if (match == null) return new UnparsedFrame(frame);
158 throw new FormatException("Couldn't parse V8 stack trace line '$frame'.");
159 }
160 157
161 // v8 location strings can be arbitrarily-nested, since it adds a layer of 158 // v8 location strings can be arbitrarily-nested, since it adds a layer of
162 // nesting for each eval performed on that line. 159 // nesting for each eval performed on that line.
163 parseLocation(location, member) { 160 parseLocation(location, member) {
164 var evalMatch = _v8EvalLocation.firstMatch(location); 161 var evalMatch = _v8EvalLocation.firstMatch(location);
165 while (evalMatch != null) { 162 while (evalMatch != null) {
166 location = evalMatch[1]; 163 location = evalMatch[1];
167 evalMatch = _v8EvalLocation.firstMatch(location); 164 evalMatch = _v8EvalLocation.firstMatch(location);
168 } 165 }
169 166
170 if (location == 'native') { 167 if (location == 'native') {
171 return new Frame(Uri.parse('native'), null, null, member); 168 return new Frame(Uri.parse('native'), null, null, member);
172 } 169 }
173 170
174 var urlMatch = _v8UrlLocation.firstMatch(location); 171 var urlMatch = _v8UrlLocation.firstMatch(location);
175 if (urlMatch == null) { 172 if (urlMatch == null) return new UnparsedFrame(frame);
176 throw new FormatException(
177 "Couldn't parse V8 stack trace line '$frame'.");
178 }
179 173
180 return new Frame( 174 return new Frame(
181 _uriOrPathToUri(urlMatch[1]), 175 _uriOrPathToUri(urlMatch[1]),
182 int.parse(urlMatch[2]), 176 int.parse(urlMatch[2]),
183 int.parse(urlMatch[3]), 177 int.parse(urlMatch[3]),
184 member); 178 member);
185 } 179 }
186 180
187 // V8 stack frames can be in two forms. 181 // V8 stack frames can be in two forms.
188 if (match[2] != null) { 182 if (match[2] != null) {
189 // The first form looks like " at FUNCTION (LOCATION)". V8 proper lists 183 // The first form looks like " at FUNCTION (LOCATION)". V8 proper lists
190 // anonymous functions within eval as "<anonymous>", while IE10 lists them 184 // anonymous functions within eval as "<anonymous>", while IE10 lists them
191 // as "Anonymous function". 185 // as "Anonymous function".
192 return parseLocation(match[2], 186 return parseLocation(match[2],
193 match[1].replaceAll("<anonymous>", "<fn>") 187 match[1].replaceAll("<anonymous>", "<fn>")
194 .replaceAll("Anonymous function", "<fn>")); 188 .replaceAll("Anonymous function", "<fn>"));
195 } else { 189 } else {
196 // The second form looks like " at LOCATION", and is used for anonymous 190 // The second form looks like " at LOCATION", and is used for anonymous
197 // functions. 191 // functions.
198 return parseLocation(match[3], "<fn>"); 192 return parseLocation(match[3], "<fn>");
199 } 193 }
200 } 194 });
201 195
202 /// Parses a string representation of a JavaScriptCore stack trace. 196 /// Parses a string representation of a JavaScriptCore stack trace.
203 factory Frame.parseJSCore(String frame) => new Frame.parseV8(frame); 197 factory Frame.parseJSCore(String frame) => new Frame.parseV8(frame);
204 198
205 /// Parses a string representation of an IE stack frame. 199 /// Parses a string representation of an IE stack frame.
206 /// 200 ///
207 /// IE10+ frames look just like V8 frames. Prior to IE10, stack traces can't 201 /// IE10+ frames look just like V8 frames. Prior to IE10, stack traces can't
208 /// be retrieved. 202 /// be retrieved.
209 factory Frame.parseIE(String frame) => new Frame.parseV8(frame); 203 factory Frame.parseIE(String frame) => new Frame.parseV8(frame);
210 204
211 /// Parses a string representation of a Firefox stack frame. 205 /// Parses a string representation of a Firefox stack frame.
212 factory Frame.parseFirefox(String frame) { 206 factory Frame.parseFirefox(String frame) => _catchFormatException(frame, () {
213 var match = _firefoxSafariFrame.firstMatch(frame); 207 var match = _firefoxSafariFrame.firstMatch(frame);
214 if (match == null) { 208 if (match == null) return new UnparsedFrame(frame);
215 throw new FormatException(
216 "Couldn't parse Firefox/Safari stack trace line '$frame'.");
217 }
218 209
219 // Normally this is a URI, but in a jsshell trace it can be a path. 210 // Normally this is a URI, but in a jsshell trace it can be a path.
220 var uri = _uriOrPathToUri(match[3]); 211 var uri = _uriOrPathToUri(match[3]);
221 212
222 var member; 213 var member;
223 if (match[1] != null) { 214 if (match[1] != null) {
224 member = match[1]; 215 member = match[1];
225 member += 216 member +=
226 new List.filled('/'.allMatches(match[2]).length, ".<fn>").join(); 217 new List.filled('/'.allMatches(match[2]).length, ".<fn>").join();
227 if (member == '') member = '<fn>'; 218 if (member == '') member = '<fn>';
228 219
229 // Some Firefox members have initial dots. We remove them for consistency 220 // Some Firefox members have initial dots. We remove them for consistency
230 // with other platforms. 221 // with other platforms.
231 member = member.replaceFirst(_initialDot, ''); 222 member = member.replaceFirst(_initialDot, '');
232 } else { 223 } else {
233 member = '<fn>'; 224 member = '<fn>';
234 } 225 }
235 226
236 var line = match[4] == '' ? null : int.parse(match[4]); 227 var line = match[4] == '' ? null : int.parse(match[4]);
237 var column = match[5] == null || match[5] == '' ? 228 var column = match[5] == null || match[5] == '' ?
238 null : int.parse(match[5]); 229 null : int.parse(match[5]);
239 return new Frame(uri, line, column, member); 230 return new Frame(uri, line, column, member);
240 } 231 });
241 232
242 /// Parses a string representation of a Safari 6.0 stack frame. 233 /// Parses a string representation of a Safari 6.0 stack frame.
243 @Deprecated("Use Frame.parseSafari instead.") 234 @Deprecated("Use Frame.parseSafari instead.")
244 factory Frame.parseSafari6_0(String frame) => new Frame.parseFirefox(frame); 235 factory Frame.parseSafari6_0(String frame) => new Frame.parseFirefox(frame);
245 236
246 /// Parses a string representation of a Safari 6.1+ stack frame. 237 /// Parses a string representation of a Safari 6.1+ stack frame.
247 @Deprecated("Use Frame.parseSafari instead.") 238 @Deprecated("Use Frame.parseSafari instead.")
248 factory Frame.parseSafari6_1(String frame) => new Frame.parseFirefox(frame); 239 factory Frame.parseSafari6_1(String frame) => new Frame.parseFirefox(frame);
249 240
250 /// Parses a string representation of a Safari stack frame. 241 /// Parses a string representation of a Safari stack frame.
251 factory Frame.parseSafari(String frame) => new Frame.parseFirefox(frame); 242 factory Frame.parseSafari(String frame) => new Frame.parseFirefox(frame);
252 243
253 /// Parses this package's string representation of a stack frame. 244 /// Parses this package's string representation of a stack frame.
254 factory Frame.parseFriendly(String frame) { 245 factory Frame.parseFriendly(String frame) => _catchFormatException(frame, () {
255 var match = _friendlyFrame.firstMatch(frame); 246 var match = _friendlyFrame.firstMatch(frame);
256 if (match == null) { 247 if (match == null) {
257 throw new FormatException( 248 throw new FormatException(
258 "Couldn't parse package:stack_trace stack trace line '$frame'."); 249 "Couldn't parse package:stack_trace stack trace line '$frame'.");
259 } 250 }
260 251
261 var uri = Uri.parse(match[1]); 252 var uri = Uri.parse(match[1]);
262 // If there's no scheme, this is a relative URI. We should interpret it as 253 // If there's no scheme, this is a relative URI. We should interpret it as
263 // relative to the current working directory. 254 // relative to the current working directory.
264 if (uri.scheme == '') { 255 if (uri.scheme == '') {
265 uri = path.toUri(path.absolute(path.fromUri(uri))); 256 uri = path.toUri(path.absolute(path.fromUri(uri)));
266 } 257 }
267 258
268 var line = match[2] == null ? null : int.parse(match[2]); 259 var line = match[2] == null ? null : int.parse(match[2]);
269 var column = match[3] == null ? null : int.parse(match[3]); 260 var column = match[3] == null ? null : int.parse(match[3]);
270 return new Frame(uri, line, column, match[4]); 261 return new Frame(uri, line, column, match[4]);
271 } 262 });
272 263
273 /// A regular expression matching an absolute URI. 264 /// A regular expression matching an absolute URI.
274 static final _uriRegExp = new RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://'); 265 static final _uriRegExp = new RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://');
275 266
276 /// A regular expression matching a Windows path. 267 /// A regular expression matching a Windows path.
277 static final _windowsRegExp = new RegExp(r'^([a-zA-Z]:[\\/]|\\\\)'); 268 static final _windowsRegExp = new RegExp(r'^([a-zA-Z]:[\\/]|\\\\)');
278 269
279 /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path, 270 /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path,
280 /// to a URI (absolute if possible). 271 /// to a URI (absolute if possible).
281 static Uri _uriOrPathToUri(String uriOrPath) { 272 static Uri _uriOrPathToUri(String uriOrPath) {
282 if (uriOrPath.contains(_uriRegExp)) { 273 if (uriOrPath.contains(_uriRegExp)) {
283 return Uri.parse(uriOrPath); 274 return Uri.parse(uriOrPath);
284 } else if (uriOrPath.contains(_windowsRegExp)) { 275 } else if (uriOrPath.contains(_windowsRegExp)) {
285 return new Uri.file(uriOrPath, windows: true); 276 return new Uri.file(uriOrPath, windows: true);
286 } else if (uriOrPath.startsWith('/')) { 277 } else if (uriOrPath.startsWith('/')) {
287 return new Uri.file(uriOrPath, windows: false); 278 return new Uri.file(uriOrPath, windows: false);
288 } 279 }
289 280
290 // As far as I've seen, Firefox and V8 both always report absolute paths in 281 // As far as I've seen, Firefox and V8 both always report absolute paths in
291 // their stack frames. However, if we do get a relative path, we should 282 // their stack frames. However, if we do get a relative path, we should
292 // handle it gracefully. 283 // handle it gracefully.
293 if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath); 284 if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath);
294 return Uri.parse(uriOrPath); 285 return Uri.parse(uriOrPath);
295 } 286 }
296 287
288 /// Runs [body] and returns its result.
289 ///
290 /// If [body] throws a [FormatException], returns an [UnparsedFrame] with
291 /// [text] instead.
292 static Frame _catchFormatException(String text, Frame body()) {
293 try {
294 return body();
295 } on FormatException catch (_) {
296 return new UnparsedFrame(text);
297 }
298 }
299
297 Frame(this.uri, this.line, this.column, this.member); 300 Frame(this.uri, this.line, this.column, this.member);
298 301
299 String toString() => '$location in $member'; 302 String toString() => '$location in $member';
300 } 303 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | lib/src/trace.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698