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

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

Powered by Google App Engine
This is Rietveld 408576698