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

Side by Side Diff: stack_trace/lib/src/stack_zone_specification.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 | « stack_trace/lib/src/lazy_trace.dart ('k') | 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 Future or Stream emits an error, the
26 /// current node is associated with that error's stack trace using the
27 /// [_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 errorCallback: errorCallback);
61 }
62
63 /// Returns the current stack chain.
64 ///
65 /// By default, the first frame of the first trace will be the line where
66 /// [currentChain] is called. If [level] is passed, the first trace will start
67 /// that many frames up instead.
68 Chain currentChain([int level=0]) => _createNode(level + 1).toChain();
69
70 /// Returns the stack chain associated with [trace], if one exists.
71 ///
72 /// The first stack trace in the returned chain will always be [trace]
73 /// (converted to a [Trace] if necessary). If there is no chain associated
74 /// with [trace], this just returns a single-trace chain containing [trace].
75 Chain chainFor(StackTrace trace) {
76 if (trace is Chain) return 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.sync();
89 var node = _createNode(level + 1);
90 future.then(completer.complete).catchError((e, stackTrace) {
91 if (stackTrace == null) stackTrace = new Trace.current();
92 if (stackTrace is! Chain && _chains[stackTrace] == null) {
93 _chains[stackTrace] = node;
94 }
95 completer.completeError(e, stackTrace);
96 });
97 return completer.future;
98 }
99
100 /// Ensures that any errors emitted by [stream] have the correct stack
101 /// information associated with them.
102 ///
103 /// By default, the first frame of the first trace will be the line where
104 /// [trackStream] is called. If [level] is passed, the first trace will start
105 /// that many frames up instead.
106 Stream trackStream(Stream stream, [int level=0]) {
107 var node = _createNode(level + 1);
108 return stream.transform(new StreamTransformer.fromHandlers(
109 handleError: (error, stackTrace, sink) {
110 if (stackTrace == null) stackTrace = new Trace.current();
111 if (stackTrace is! Chain && _chains[stackTrace] == null) {
112 _chains[stackTrace] = node;
113 }
114 sink.addError(error, stackTrace);
115 }));
116 }
117
118 /// Tracks the current stack chain so it can be set to [_currentChain] when
119 /// [f] is run.
120 ZoneCallback registerCallback(Zone self, ZoneDelegate parent, Zone zone,
121 Function f) {
122 if (f == null) return parent.registerCallback(zone, null);
123 var node = _createNode(1);
124 return parent.registerCallback(zone, () => _run(f, node));
125 }
126
127 /// Tracks the current stack chain so it can be set to [_currentChain] when
128 /// [f] is run.
129 ZoneUnaryCallback registerUnaryCallback(Zone self, ZoneDelegate parent,
130 Zone zone, Function f) {
131 if (f == null) return parent.registerUnaryCallback(zone, null);
132 var node = _createNode(1);
133 return parent.registerUnaryCallback(zone, (arg) {
134 return _run(() => f(arg), node);
135 });
136 }
137
138 /// Tracks the current stack chain so it can be set to [_currentChain] when
139 /// [f] is run.
140 ZoneBinaryCallback registerBinaryCallback(Zone self, ZoneDelegate parent,
141 Zone zone, Function f) {
142 if (f == null) return parent.registerBinaryCallback(zone, null);
143 var node = _createNode(1);
144 return parent.registerBinaryCallback(zone, (arg1, arg2) {
145 return _run(() => f(arg1, arg2), node);
146 });
147 }
148
149 /// Looks up the chain associated with [stackTrace] and passes it either to
150 /// [_onError] or [parent]'s error handler.
151 handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, error,
152 StackTrace stackTrace) {
153 var stackChain = chainFor(stackTrace);
154 if (_onError == null) {
155 return parent.handleUncaughtError(zone, error, stackChain);
156 }
157
158 // TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just
159 // allow [runBinary] to throw instead once issue 18134 is fixed.
160 try {
161 return parent.runBinary(zone, _onError, error, stackChain);
162 } catch (newError, newStackTrace) {
163 if (identical(newError, error)) {
164 return parent.handleUncaughtError(zone, error, stackChain);
165 } else {
166 return parent.handleUncaughtError(zone, newError, newStackTrace);
167 }
168 }
169 }
170
171 /// Attaches the current stack chain to [stackTrace], replacing it if
172 /// necessary.
173 AsyncError errorCallback(Zone self, ZoneDelegate parent, Zone zone,
174 Object error, StackTrace stackTrace) {
175 // Go up two levels to get through [_CustomZone.errorCallback].
176 if (stackTrace == null) {
177 stackTrace = _createNode(2).toChain();
178 } else {
179 if (_chains[stackTrace] == null) _chains[stackTrace] = _createNode(2);
180 }
181
182 var asyncError = parent.errorCallback(zone, error, stackTrace);
183 return asyncError == null ? new AsyncError(error, stackTrace) : asyncError;
184 }
185
186 /// Creates a [_Node] with the current stack trace and linked to
187 /// [_currentNode].
188 ///
189 /// By default, the first frame of the first trace will be the line where
190 /// [_createNode] is called. If [level] is passed, the first trace will start
191 /// that many frames up instead.
192 _Node _createNode([int level=0]) =>
193 new _Node(new Trace.current(level + 1), _currentNode);
194
195 // TODO(nweiz): use a more robust way of detecting and tracking errors when
196 // issue 15105 is fixed.
197 /// Runs [f] with [_currentNode] set to [node].
198 ///
199 /// If [f] throws an error, this associates [node] with that error's stack
200 /// trace.
201 _run(Function f, _Node node) {
202 var previousNode = _currentNode;
203 _currentNode = node;
204 try {
205 return f();
206 } catch (e, stackTrace) {
207 _chains[stackTrace] = node;
208 rethrow;
209 } finally {
210 _currentNode = previousNode;
211 }
212 }
213 }
214
215 /// A linked list node representing a single entry in a stack chain.
216 class _Node {
217 /// The stack trace for this link of the chain.
218 final Trace trace;
219
220 /// The previous node in the chain.
221 final _Node previous;
222
223 _Node(StackTrace trace, [this.previous])
224 : trace = trace == null ? new Trace.current() : new Trace.from(trace);
225
226 /// Converts this to a [Chain].
227 Chain toChain() {
228 var nodes = <Trace>[];
229 var node = this;
230 while (node != null) {
231 nodes.add(node.trace);
232 node = node.previous;
233 }
234 return new Chain(nodes);
235 }
236 }
OLDNEW
« no previous file with comments | « stack_trace/lib/src/lazy_trace.dart ('k') | stack_trace/lib/src/trace.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698