Chromium Code Reviews| Index: pkg/stack_trace/lib/src/trace.dart |
| diff --git a/pkg/stack_trace/lib/src/trace.dart b/pkg/stack_trace/lib/src/trace.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..15b89a633185738d496a7a12b8ae73c39c67bf47 |
| --- /dev/null |
| +++ b/pkg/stack_trace/lib/src/trace.dart |
| @@ -0,0 +1,124 @@ |
| +// 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 trace; |
| + |
| +import 'dart:uri'; |
| + |
| +import 'frame.dart'; |
| + |
| +/// A stack trace, comprised of a list of stack frames. |
| +class Trace implements StackTrace { |
| + // TODO(nweiz): make this read-only once issue 8321 is fixed. |
| + /// The stack frames that comprise this stack trace. |
| + final List<Frame> frames; |
| + |
| + /// Returns a human-readable representation of [stackTrace]. If [terse] is |
| + /// set, this folds together multiple stack frames from the Dart core |
| + /// libraries, so that only the core library method directly called from user |
| + /// code is visible (see [Trace.terse]). |
| + static String format(StackTrace stackTrace, {bool terse: true}) { |
| + var trace = new Trace.from(stackTrace); |
| + if (terse) trace = trace.terse; |
| + return trace.toString(); |
| + } |
| + |
| + /// Returns the current stack trace. |
| + /// |
| + /// By default, the first frame of this trace will be the line where |
| + /// [Trace.current] is called. If [level] is passed, the trace will start that |
| + /// many frames up instead. |
| + factory Trace.current([int level=0]) { |
| + if (level < 0) { |
| + throw new ArgumentError("Argument [level] must be greater than or equal " |
| + "to 0."); |
| + } |
| + |
| + try { |
| + throw ''; |
| + } catch (_, nativeTrace) { |
| + var trace = new Trace.from(nativeTrace); |
| + return new Trace(trace.frames.skip(level + 1)); |
| + } |
| + } |
| + |
| + /// Returns a new stack trace containing the same data as [trace]. |
| + /// |
| + /// If [trace] is a native [StackTrace], its data will be parsed out; if it's |
| + /// a [Trace], it will be returned as-is. |
| + factory Trace.from(StackTrace trace) { |
| + if (trace is Trace) return trace; |
| + return new Trace.parse(trace.fullStackTrace); |
| + } |
| + |
| + /// Parses a string representation of a stack trace. |
| + /// |
| + /// [trace] should be formatted in the same way as native stack traces. |
| + Trace.parse(String trace) |
| + : this(trace.trim().split("\n").map((line) => new Frame.parse(line))); |
| + |
| + /// Returns a new [Trace] comprised of [frames]. |
| + Trace(Iterable<Frame> frames) |
| + : frames = frames.toList(); |
| + |
| + // TODO(nweiz): Keep track of which [Frame]s are part of the partial stack |
| + // trace and only print them. |
| + /// Returns a string representation of this stack trace. |
| + /// |
| + /// This is identical to [toString]. It will not be formatted in the manner of |
| + /// native stack traces. |
| + String get stackTrace => toString(); |
| + |
| + /// Returns a string representation of this stack trace. |
| + /// |
| + /// This is identical to [toString]. It will not be formatted in the manner of |
| + /// native stack traces. |
| + String get fullStackTrace => toString(); |
| + |
| + /// Returns a terser version of [this]. This is accomplished by folding |
| + /// together multiple stack frames from the core library. If multiple such |
| + /// frames appear in a row, only the last (the one directly called by user |
| + /// code) is kept. Core library patches are also renamed to remove their |
| + /// `-patch` suffix. |
| + Trace get terse { |
| + return new Trace(frames.reversed.reduce(<Frame>[], (newFrames, frame) { |
| + if (!frame.isCore) { |
| + newFrames.add(frame); |
|
Bob Nystrom
2013/03/27 00:46:44
Given that you are imperatively building up a resu
nweiz
2013/03/27 21:15:46
Done.
|
| + } else if (newFrames.isEmpty || !newFrames.last.isCore) { |
| + var library = frame.library.replaceAll(new RegExp(r"-patch$"), ''); |
|
Bob Nystrom
2013/03/27 00:46:44
Hoist this regex out to a top level final variable
nweiz
2013/03/27 21:15:46
I really hate that we have to do this for every re
|
| + newFrames.add(new Frame( |
| + Uri.parse(library), frame.line, frame.column, frame.member)); |
| + } |
| + |
| + return newFrames; |
| + }).reversed); |
| + } |
| + |
| + /// Returns a human-readable string representation of [this]. |
| + String toString() { |
| + if (frames.length == '') return ''; |
| + |
| + // Figure out the longest path so we know how much to pad. |
| + var longest = frames.map((frame) => frame.location.length).max(); |
| + |
| + // Print out the stack trace nicely formatted. |
| + return frames.map((frame) { |
| + return '${_padRight(frame.location, longest)} ${frame.member}\n'; |
| + }).join(); |
| + } |
| + |
| + /// Returns [string] with enough spaces added to the end to make it [length] |
| + /// characters long. |
| + String _padRight(String string, int length) { |
|
Bob Nystrom
2013/03/27 00:46:44
Make a top-level function? It doesn't access any i
nweiz
2013/03/27 21:15:46
Done.
|
| + if (string.length >= length) return string; |
| + |
| + var result = new StringBuffer(); |
| + result.write(string); |
| + for (var i = 0; i < length - string.length; i++) { |
| + result.write(' '); |
| + } |
| + |
| + return result.toString(); |
| + } |
| +} |