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

Side by Side Diff: sky/tests/resources/third_party/stack_trace/src/stack_zone_specification.dart

Issue 921863002: Add testing resources from SkyDart branch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: move to third_party Created 5 years, 10 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
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 var asyncError = parent.errorCallback(zone, error, stackTrace);
176 if (asyncError != null) {
177 error = asyncError.error;
178 stackTrace = asyncError.stackTrace;
179 }
180
181 // Go up two levels to get through [_CustomZone.errorCallback].
182 if (stackTrace == null) {
183 stackTrace = _createNode(2).toChain();
184 } else {
185 if (_chains[stackTrace] == null) _chains[stackTrace] = _createNode(2);
186 }
187
188 return new AsyncError(error, stackTrace);
189 }
190
191 /// Creates a [_Node] with the current stack trace and linked to
192 /// [_currentNode].
193 ///
194 /// By default, the first frame of the first trace will be the line where
195 /// [_createNode] is called. If [level] is passed, the first trace will start
196 /// that many frames up instead.
197 _Node _createNode([int level=0]) =>
198 new _Node(new Trace.current(level + 1), _currentNode);
199
200 // TODO(nweiz): use a more robust way of detecting and tracking errors when
201 // issue 15105 is fixed.
202 /// Runs [f] with [_currentNode] set to [node].
203 ///
204 /// If [f] throws an error, this associates [node] with that error's stack
205 /// trace.
206 _run(Function f, _Node node) {
207 var previousNode = _currentNode;
208 _currentNode = node;
209 try {
210 return f();
211 } catch (e, stackTrace) {
212 _chains[stackTrace] = node;
213 rethrow;
214 } finally {
215 _currentNode = previousNode;
216 }
217 }
218 }
219
220 /// A linked list node representing a single entry in a stack chain.
221 class _Node {
222 /// The stack trace for this link of the chain.
223 final Trace trace;
224
225 /// The previous node in the chain.
226 final _Node previous;
227
228 _Node(StackTrace trace, [this.previous])
229 : trace = trace == null ? new Trace.current() : new Trace.from(trace);
230
231 /// Converts this to a [Chain].
232 Chain toChain() {
233 var nodes = <Trace>[];
234 var node = this;
235 while (node != null) {
236 nodes.add(node.trace);
237 node = node.previous;
238 }
239 return new Chain(nodes);
240 }
241 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698