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

Side by Side Diff: pkg/stack_trace/lib/src/stack_zone_specification.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: Support Trace.from(Chain) 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.stack_zone_specification;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import 'trace.dart';
11 import 'chain.dart';
12
13 /// A class encapsulating the zone specification for a [Chain.capture] zone.
14 ///
15 /// Until they're materialized and exposed to the user, stack chains are tracked
16 /// as linked lists of [Trace]s using the [_Node] class. These nodes are stored
17 /// in three distinct ways:
18 ///
19 /// * When a callback is registered, a node is created and stored as a captured
20 /// local variable until the callback is run.
21 ///
22 /// * When a callback is run, its captured node is set as the [_currentNode] so
23 /// it can be available to [Chain.current] and to be linked into additional
24 /// chains when more callbacks are scheduled.
25 ///
26 /// * When a callback throws an error or a [Chain.track]ed Future or Stream
27 /// emits an error, the current node is associated with that error's stack
28 /// trace using the [_chains] expando.
29 ///
30 /// Since [ZoneSpecification] can't be extended or even implemented, in order to
31 /// get a real [ZoneSpecification] instance it's necessary to call [toSpec].
32 class StackZoneSpecification {
33 /// The expando that associates stack chains with [StackTrace]s.
34 ///
35 /// The chains are associated with stack traces rather than errors themselves
36 /// because it's a common practice to throw strings as errors, which can't be
37 /// used with expandos.
38 ///
39 /// The chain associated with a given stack trace doesn't contain a node for
40 /// that stack trace.
41 final _chains = new Expando<_Node>("stack chains");
42
43 /// The error handler for the zone.
44 ///
45 /// If this is null, that indicates that any unhandled errors should be passed
46 /// to the parent zone.
47 final ChainHandler _onError;
48
49 /// The front-most node of the current stack chain.
50 _Node _currentNode;
51
52 StackZoneSpecification([this._onError])
53 : super();
54
55 /// Converts [this] to a real [ZoneSpecification].
56 ZoneSpecification toSpec() {
57 return new ZoneSpecification(
58 handleUncaughtError: handleUncaughtError,
59 registerCallback: registerCallback,
60 registerUnaryCallback: registerUnaryCallback,
61 registerBinaryCallback: registerBinaryCallback);
62 }
63
64 /// Returns the current stack chain.
65 ///
66 /// By default, the first frame of the first trace will be the line where
67 /// [currentChain] is called. If [level] is passed, the first trace will start
68 /// that many frames up instead.
69 Chain currentChain([int level=0]) => _createNode(level + 1).toChain();
70
71 /// Returns the stack chain associated with, [trace], if one exists.
72 ///
73 /// The first stack trace in the returned chain will always be [trace]
74 /// (converted to a [Trace] if necessary). If there is no chain associated
75 /// with [trace], this just returns a single-trace chain containing [trace].
76 Chain chainFor(StackTrace trace) {
77 var previous = trace == null ? null : _chains[trace];
78 return new _Node(trace, previous).toChain();
79 }
80
81 /// Ensures that an error emitted by [future] has the correct stack
82 /// information associated with it.
83 ///
84 /// By default, the first frame of the first trace will be the line where
85 /// [trackFuture] is called. If [level] is passed, the first trace will start
86 /// that many frames up instead.
87 Future trackFuture(Future future, [int level=0]) {
88 var completer = new Completer();
89 var node = _createNode(level + 1);
90 future.then(completer.complete).catchError((e, stackTrace) {
91 if (stackTrace == null) stackTrace = new Trace.current();
92 if (_chains[stackTrace] == null) _chains[stackTrace] = node;
93 completer.completeError(e, stackTrace);
94 });
95 return completer.future;
96 }
97
98 /// Ensures that any errors emitted by [stream] have the correct stack
99 /// information associated with them.
100 ///
101 /// By default, the first frame of the first trace will be the line where
102 /// [trackStream] is called. If [level] is passed, the first trace will start
103 /// that many frames up instead.
104 Stream trackStream(Stream stream, [int level=0]) {
105 var node = _createNode(level + 1);
106 return stream.transform(new StreamTransformer.fromHandlers(
107 handleError: (error, stackTrace, sink) {
108 if (stackTrace == null) stackTrace = new Trace.current();
109 if (_chains[stackTrace] == null) _chains[stackTrace] = node;
110 sink.addError(error, stackTrace);
111 }));
112 }
113
114 /// Tracks the current stack chain so it can be set to [_currentChain] when
115 /// [f] is run.
116 ZoneCallback registerCallback(Zone self, ZoneDelegate parent, Zone zone,
117 Function f) {
118 if (f == null) return parent.registerCallback(zone, null);
119 var node = _createNode(1);
120 return parent.registerCallback(zone, () => _run(f, node));
121 }
122
123 /// Tracks the current stack chain so it can be set to [_currentChain] when
124 /// [f] is run.
125 ZoneUnaryCallback registerUnaryCallback(Zone self, ZoneDelegate parent,
126 Zone zone, Function f) {
127 if (f == null) return parent.registerUnaryCallback(zone, null);
128 var node = _createNode(1);
129 return parent.registerUnaryCallback(zone, (arg) {
130 return _run(() => f(arg), node);
131 });
132 }
133
134 /// Tracks the current stack chain so it can be set to [_currentChain] when
135 /// [f] is run.
136 ZoneBinaryCallback registerBinaryCallback(Zone self, ZoneDelegate parent,
137 Zone zone, Function f) {
138 if (f == null) return parent.registerBinaryCallback(zone, null);
139 var node = _createNode(1);
140 return parent.registerBinaryCallback(zone, (arg1, arg2) {
141 return _run(() => f(arg1, arg2), node);
142 });
143 }
144
145 /// Looks up the chain associated with [stackTrace] and passes it either to
146 /// [_onError] or [parent]'s error handler.
147 handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, error,
148 StackTrace stackTrace) {
149 if (_onError == null) {
150 return parent.handleUncaughtError(zone, error, chainFor(stackTrace));
151 } else {
152 _onError(error, chainFor(stackTrace));
153 }
154 }
155
156 /// Creates a [_Node] with the current stack trace and linked to
157 /// [_currentNode].
158 ///
159 /// By default, the first frame of the first trace will be the line where
160 /// [_createNode] is called. If [level] is passed, the first trace will start
161 /// that many frames up instead.
162 _Node _createNode([int level=0]) =>
163 new _Node(new Trace.current(level + 1), _currentNode);
164
165 // TODO(nweiz): use a more robust way of detecting and tracking errors when
166 // issue 15105 is fixed.
167 /// Runs [f] with [_currentNode] set to [node].
168 ///
169 /// If [f] throws an error, this associates [node] with that error's stack
170 /// trace.
171 _run(Function f, _Node node) {
172 var previousNode = _currentNode;
173 _currentNode = node;
174 try {
175 return f();
176 } catch (e, stackTrace) {
177 _chains[stackTrace] = node;
178 rethrow;
179 } finally {
180 _currentNode = previousNode;
181 }
182 }
183 }
184
185 /// A linked list node representing a single entry in a stack chain.
186 class _Node {
187 /// The stack trace for this link of the chain.
188 final Trace trace;
189
190 /// The previous node in the chain.
191 final _Node previous;
192
193 _Node(StackTrace trace, [this.previous])
194 : trace = trace == null ? new Trace.current() : new Trace.from(trace);
195
196 /// Converts this to a [Chain].
197 Chain toChain() {
198 var queue = new Queue<Trace>();
199 var node = this;
200 while (node != null) {
201 queue.add(node.trace);
202 node = node.previous;
203 }
204 return new Chain(queue);
205 }
206 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698