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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/pkg.status ('k') | pkg/stack_trace/lib/src/stack_zone_specification.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/stack_trace/lib/src/chain.dart
diff --git a/pkg/stack_trace/lib/src/chain.dart b/pkg/stack_trace/lib/src/chain.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f6e2f3fb33ae05eef135e63e688a40cbf5eedb27
--- /dev/null
+++ b/pkg/stack_trace/lib/src/chain.dart
@@ -0,0 +1,173 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library stack_trace.chain;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'stack_zone_specification.dart';
+import 'trace.dart';
+import 'utils.dart';
+
+/// A function that handles errors in the zone wrapped by [Chain.capture].
+typedef void ChainHandler(error, Chain chain);
+
+/// A chain of stack traces.
+///
+/// A stack chain is a collection of one or more stack traces that collectively
+/// represent the path from [main] through nested function calls to a particular
+/// code location, usually where an error was thrown. Multiple stack traces are
+/// necessary when using asynchronous functions, since the program's stack is
+/// reset before each asynchronous callback is run.
+///
+/// Stack chains can be automatically tracked using [Chain.capture]. This sets
+/// up a new [Zone] in which the current stack chain is tracked and can be
+/// accessed using [new Chain.current]. Any errors that would be top-leveled in
+/// the zone can be handled, along with their associated chains, with the
+/// `onError` callback.
+///
+/// For the most part [Chain.capture] will notice when an error is thrown and
+/// associate the correct stack chain with it; the chain can be accessed using
+/// [new Chain.forTrace]. However, there are some cases where exceptions won't
+/// be automatically detected: any [Future] constructor,
+/// [Completer.completeError], [Stream.addError], and libraries that use these.
+/// For these, all you need to do is wrap the Future or Stream in a call to
+/// [Chain.track] and the errors will be tracked correctly.
+class Chain implements StackTrace {
+ /// The line used in the string representation of stack chains to represent
+ /// the gap between traces.
+ static const _GAP = '===== asynchronous gap ===========================\n';
+
+ /// The stack traces that make up this chain.
+ ///
+ /// Like the frames in a stack trace, the traces are ordered from most local
+ /// to least local. The first one is the trace where the actual exception was
+ /// raised, the second one is where that callback was scheduled, and so on.
+ final List<Trace> traces;
+
+ /// The [StackZoneSpecification] for the current zone.
+ static StackZoneSpecification get _currentSpec =>
+ Zone.current[#stack_trace.stack_zone.spec];
+
+ /// Runs [callback] in a [Zone] in which the current stack chain is tracked
+ /// and automatically associated with (most) errors.
+ ///
+ /// If [onError] is passed, any error in the zone that would otherwise go
+ /// unhandled is passed to it, along with the [Chain] associated with that
+ /// error. Note that if [callback] produces multiple unhandled errors,
+ /// [onError] may be called more than once. If [onError] isn't passed, the
+ /// parent Zone's `unhandledErrorHandler` will be called with the error and
+ /// its chain.
+ ///
+ /// For the most part an error thrown in the zone will have the correct stack
+ /// chain associated with it. However, there are some cases where exceptions
+ /// won't be automatically detected: any [Future] constructor,
+ /// [Completer.completeError], [Stream.addError], and libraries that use
+ /// these. For these, all you need to do is wrap the Future or Stream in a
+ /// call to [Chain.track] and the errors will be tracked correctly.
+ ///
+ /// Note that even if [onError] isn't passed, this zone will still be an error
+ /// zone. This means that any errors that would cross the zone boundary are
+ /// considered unhandled.
+ ///
+ /// If [callback] returns a value, it will be returned by [capture] as well.
+ ///
+ /// Currently, capturing stack chains doesn't work when using dart2js due to
+ /// issues [15171] and [15105]. Stack chains reported on dart2js will contain
+ /// only one trace.
+ ///
+ /// [15171]: https://code.google.com/p/dart/issues/detail?id=15171
+ /// [15105]: https://code.google.com/p/dart/issues/detail?id=15105
+ static capture(callback(), {ChainHandler onError}) {
+ var spec = new StackZoneSpecification(onError);
+ return runZoned(callback, zoneSpecification: spec.toSpec(), zoneValues: {
+ #stack_trace.stack_zone.spec: spec
+ });
+ }
+
+ /// Ensures that any errors emitted by [futureOrStream] have the correct stack
+ /// chain information associated with them.
+ ///
+ /// For the most part an error thrown within a [capture] zone will have the
+ /// correct stack chain automatically associated with it. However, there are
+ /// some cases where exceptions won't be automatically detected: any [Future]
+ /// constructor, [Completer.completeError], [Stream.addError], and libraries
+ /// that use these.
+ ///
+ /// This returns a [Future] or [Stream] that will emit the same values and
+ /// errors as [futureOrStream]. The only exception is that if [futureOrStream]
+ /// emits an error without a stack trace, one will be added in the return
+ /// value.
+ ///
+ /// If this is called outside of a [capture] zone, it just returns
+ /// [futureOrStream] as-is.
+ ///
+ /// As the name suggests, [futureOrStream] may be either a [Future] or a
+ /// [Stream].
+ static track(futureOrStream) {
+ if (_currentSpec == null) return futureOrStream;
+ if (futureOrStream is Future) {
+ return _currentSpec.trackFuture(futureOrStream, 1);
+ } else {
+ return _currentSpec.trackStream(futureOrStream, 1);
+ }
+ }
+
+ /// Returns the current stack chain.
+ ///
+ /// By default, the first frame of the first trace will be the line where
+ /// [Chain.current] is called. If [level] is passed, the first trace will
+ /// start that many frames up instead.
+ ///
+ /// If this is called outside of a [capture] zone, it just returns a
+ /// single-trace chain.
+ factory Chain.current([int level=0]) {
+ if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
+ return new Chain([new Trace.current(level + 1)]);
+ }
+
+ /// Returns the stack chain associated with [trace].
+ ///
+ /// The first stack trace in the returned chain will always be [trace]
+ /// (converted to a [Trace] if necessary). If there is no chain associated
+ /// with [trace] or if this is called outside of a [capture] zone, this just
+ /// returns a single-trace chain containing [trace].
+ ///
+ /// If [trace] is already a [Chain], it will be returned as-is.
+ factory Chain.forTrace(StackTrace trace) {
+ if (trace is Chain) return trace;
+ if (_currentSpec == null) return new Chain([new Trace.from(trace)]);
+ return _currentSpec.chainFor(trace);
+ }
+
+ /// Parses a string representation of a stack chain.
+ ///
+ /// Specifically, this parses the output of [Chain.toString].
+ factory Chain.parse(String chain) =>
+ new Chain(chain.split(_GAP).map((trace) => new Trace.parseFriendly(trace)));
+
+ /// Returns a new [Chain] comprised of [traces].
+ Chain(Iterable<Trace> traces)
+ : traces = new UnmodifiableListView<Trace>(traces.toList());
+
+ /// Returns a terser version of [this].
+ ///
+ /// This calls [Trace.terse] on every trace in [traces], and discards any
+ /// trace that contain only internal frames.
+ Chain get terse {
+ return new Chain(traces.map((trace) => trace.terse).where((trace) {
+ // Ignore traces that contain only internal processing.
+ return trace.frames.length > 1;
+ }));
+ }
+
+ /// Converts [this] to a [Trace].
+ ///
+ /// The trace version of a chain is just the concatenation of all the traces
+ /// in the chain.
+ Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames)));
+
+ String toString() => traces.join(_GAP);
+}
« no previous file with comments | « pkg/pkg.status ('k') | pkg/stack_trace/lib/src/stack_zone_specification.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698