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

Side by Side Diff: pkg/stack_trace/lib/src/chain.dart

Issue 75863004: Add a stack chain class to the stack trace package. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library stack_trace.chain;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import 'stack_zone_specification.dart';
11 import 'trace.dart';
12 import 'utils.dart';
13
14 /// A function that handles errors in the zone wrapped by [Chain.capture].
15 typedef void ChainHandler(error, Chain chain);
16
17 /// A chain of stack traces.
18 ///
19 /// A stack chain is a collection of one or more stack traces that collectively
20 /// represent the path from [main] through nested function calls to a particular
21 /// code location, usually where an error was thrown. Multiple stack traces are
22 /// necessary when using asynchronous functions, since the program's stack is
23 /// reset as each asynchronous callback is run.
floitsch 2013/11/21 15:28:08 "as" feels wrong to me.
nweiz 2013/11/22 00:50:52 Changed to "before".
24 ///
25 /// Stack chains can be automatically tracked using [Chain.capture]. This sets
26 /// up a new [Zone] in which the current stack chain is tracked and can be
27 /// accessed using [new Chain.current]. Any errors that would be top-leveled in
28 /// the zone can be handled, along with their associated chains, with the
29 /// `onError` callback.
30 ///
31 /// For the most part [Chain.capture] will notice when an error is thrown and
32 /// associate the correct stack chain with it; the chain can be accessed using
33 /// [new Chain.forTrace]. However, there are some cases where exceptions won't
34 /// be automatically detected: any [Future] constructor,
35 /// [Completer.completeError], [Stream.addError], and libraries that use these.
36 /// For these, all you need to do is wrap the Future or Stream in a call to
37 /// [Chain.track] and the errors will be tracked correctly.
38 class Chain implements StackTrace {
39 /// The line used in the string representation of stack chains to represent
40 /// the gap between traces.
41 static const _GAP = '===== asynchronous gap ===========================\n';
42
43 /// The stack traces that make up this chain.
44 ///
45 /// Like the frames in a stack trace, the traces are ordered from most local
46 /// to least local. The first one is the trace where the actual exception was
47 /// raised, the second one is where that callback was scheduled, and so on.
48 final List<Trace> traces;
49
50 /// The [StackZoneSpecification] for the current zone.
51 static StackZoneSpecification get _currentSpec =>
52 Zone.current[#stack_trace.stack_zone.spec];
floitsch 2013/11/21 15:28:08 missing indent.
nweiz 2013/11/22 00:50:52 I like using two spaces here because this is basic
53
54 /// Runs [callback] in a [Zone] in which the current stack chain is tracked
55 /// and automatically associated with (most) errors.
56 ///
57 /// If [onError] is passed, any error in the zone that would otherwise go
58 /// unhandled is passed to it, along with the [Chain] associated with that
59 /// error. Note that if [callback] produces multiple unhandled errors,
60 /// [onError] may be called more than once. If [onError] isn't passed, the
61 /// parent Zone's `unhandledErrorHandler` will be called with the error and
62 /// its chain.
63 ///
64 /// For the most part an error thrown in the zone will have the correct stack
65 /// chain associated with it. However, there are some cases where exceptions
66 /// won't be automatically detected: any [Future] constructor,
67 /// [Completer.completeError], [Stream.addError], and libraries that use
68 /// these. For these, all you need to do is wrap the Future or Stream in a
69 /// call to [Chain.track] and the errors will be tracked correctly.
70 ///
71 /// If [callback] returns a value, it will be returned by [capture] as well.
72 ///
73 /// Currently, capturing stack chains doesn't work when using dart2js due to
74 /// issues [15171] and [15105]. Stack chains reported on dart2js will contain only
floitsch 2013/11/21 15:28:08 long line.
nweiz 2013/11/22 00:50:52 Done.
75 /// one trace.
76 ///
77 /// [15171]: https://code.google.com/p/dart/issues/detail?id=15171
78 /// [15105]: https://code.google.com/p/dart/issues/detail?id=15105
79 static capture(callback(), {ChainHandler onError}) {
80 var spec = new StackZoneSpecification(onError);
81 return runZoned(callback, zoneSpecification: spec.toSpec(), zoneValues: {
82 #stack_trace.stack_zone.spec: spec
83 });
84 }
85
86 /// Ensures that any errors emitted by [futureOrStream] have the correct stack
87 /// chain information associated with them.
88 ///
89 /// For the most part an error thrown within a [capture] zone will have the
90 /// correct stack chain automatically associated with it. However, there are
91 /// some cases where exceptions won't be automatically detected: any [Future]
92 /// constructor, [Completer.completeError], [Stream.addError], and libraries
93 /// that use these.
94 ///
95 /// This returns a [Future] or [Stream] that will emit the same values and
96 /// errors as [futureOrStream]. The only exception is that if [futureOrStream]
97 /// emits an error without a stack trace, one will be added in the return
98 /// value.
99 ///
100 /// If this is called outside of a [capture] zone, it just returns
101 /// [futureOrStream] as-is.
102 ///
103 /// As the name suggests, [futureOrStream] may be either a [Future] or a
104 /// [Stream].
105 static track(futureOrStream) {
106 if (_currentSpec == null) return futureOrStream;
107 if (futureOrStream is Future) {
108 return _currentSpec.trackFuture(futureOrStream, 1);
109 } else {
110 return _currentSpec.trackStream(futureOrStream, 1);
111 }
112 }
113
114 /// Returns the current stack chain.
115 ///
116 /// By default, the first frame of the first trace will be the line where
117 /// [Chain.current] is called. If [level] is passed, the first trace will
118 /// start that many frames up instead.
119 ///
120 /// If this is called outside of a [capture] zone, it just returns a
121 /// single-trace chain.
122 factory Chain.current([int level=0]) {
123 if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
124 return new Chain([new Trace.current(level + 1)]);
125 }
126
127 /// Returns the stack chain associated with [trace].
128 ///
129 /// The first stack trace in the returned chain will always be [trace]
130 /// (converted to a [Trace] if necessary). If there is no chain associated
131 /// with [trace] or if this is called outside of a [capture] zone, this just
132 /// returns a single-trace chain containing [trace].
133 ///
134 /// If [trace] is already a [Chain], it will be returned as-is.
135 factory Chain.forTrace(StackTrace trace) {
136 if (trace is Chain) return trace;
137 if (_currentSpec == null) return new Chain([new Trace.from(trace)]);
138 return _currentSpec.chainFor(trace);
139 }
140
141 /// Parses a string representation of a stack chain.
142 ///
143 /// Specifically, this parses the output of [Chain.toString].
144 factory Chain.parse(String chain) =>
145 new Chain(chain.split(_GAP).map((trace) => new Trace.parseFriendly(trace)));
146
147 /// Returns a new [Chain] comprised of [traces].
148 Chain(Iterable<Trace> traces)
149 : traces = new UnmodifiableListView<Trace>(traces.toList());
150
151 /// Returns a terser version of [this].
152 ///
153 /// This calls [Trace.terse] on every trace in [traces], and discards any
154 /// trace that contain only internal frames.
155 Chain get terse {
156 return new Chain(traces.map((trace) => trace.terse).where((trace) {
157 // Ignore traces that contain only internal processing.
158 return trace.frames.length > 1;
159 }));
160 }
161
162 /// Converts [this] to a [Trace].
163 ///
164 /// The trace version of a chain is just the concatenation of all the traces
165 /// in the chain.
166 Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames)));
167
168 String toString() => traces.join(_GAP);
169 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698