Index: packages/stack_trace/lib/src/chain.dart |
diff --git a/packages/stack_trace/lib/src/chain.dart b/packages/stack_trace/lib/src/chain.dart |
index 2e07d4bb9c776b81e50647d60a9e449cdbd89ad0..2d1349de2076f0b9dc685b9274dbe8bfc3935a66 100644 |
--- a/packages/stack_trace/lib/src/chain.dart |
+++ b/packages/stack_trace/lib/src/chain.dart |
@@ -2,20 +2,22 @@ |
// 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 'dart:math' as math; |
import 'frame.dart'; |
+import 'lazy_chain.dart'; |
import 'stack_zone_specification.dart'; |
import 'trace.dart'; |
import 'utils.dart'; |
/// A function that handles errors in the zone wrapped by [Chain.capture]. |
+@Deprecated("Will be removed in stack_trace 2.0.0.") |
typedef void ChainHandler(error, Chain chain); |
+/// An opaque key used to track the current [StackZoneSpecification]. |
+final _specKey = new Object(); |
+ |
/// A chain of stack traces. |
/// |
/// A stack chain is a collection of one or more stack traces that collectively |
@@ -37,7 +39,6 @@ typedef void ChainHandler(error, Chain chain); |
/// "$stackChain"); |
/// }); |
class Chain implements StackTrace { |
- |
/// The stack traces that make up this chain. |
/// |
/// Like the frames in a stack trace, the traces are ordered from most local |
@@ -46,11 +47,16 @@ class Chain implements StackTrace { |
final List<Trace> traces; |
/// The [StackZoneSpecification] for the current zone. |
- static StackZoneSpecification get _currentSpec => |
- Zone.current[#stack_trace.stack_zone.spec]; |
+ static StackZoneSpecification get _currentSpec => Zone.current[_specKey]; |
- /// Runs [callback] in a [Zone] in which the current stack chain is tracked |
- /// and automatically associated with (most) errors. |
+ /// If [when] is `true`, runs [callback] in a [Zone] in which the current |
+ /// stack chain is tracked and automatically associated with (most) errors. |
+ /// |
+ /// If [when] is `false`, this does not track stack chains. Instead, it's |
+ /// identical to [runZoned], except that it wraps any errors in [new |
+ /// Chain.forTrace]—which will only wrap the trace unless there's a different |
+ /// [Chain.capture] active. This makes it easy for the caller to only capture |
+ /// stack chains in debug mode or during development. |
/// |
/// 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 |
@@ -64,7 +70,23 @@ class Chain implements StackTrace { |
/// considered unhandled. |
/// |
/// If [callback] returns a value, it will be returned by [capture] as well. |
- static capture(callback(), {ChainHandler onError}) { |
+ static T capture<T>(T callback(), |
+ {void onError(error, Chain chain), bool when: true}) { |
+ if (!when) { |
+ var newOnError; |
+ if (onError != null) { |
+ newOnError = (error, stackTrace) { |
+ onError( |
+ error, |
+ stackTrace == null |
+ ? new Chain.current() |
+ : new Chain.forTrace(stackTrace)); |
+ }; |
+ } |
+ |
+ return runZoned(callback, onError: newOnError); |
+ } |
+ |
var spec = new StackZoneSpecification(onError); |
return runZoned(() { |
try { |
@@ -73,9 +95,20 @@ class Chain implements StackTrace { |
// TODO(nweiz): Don't special-case this when issue 19566 is fixed. |
return Zone.current.handleUncaughtError(error, stackTrace); |
} |
- }, zoneSpecification: spec.toSpec(), zoneValues: { |
- #stack_trace.stack_zone.spec: spec |
- }); |
+ }, |
+ zoneSpecification: spec.toSpec(), |
+ zoneValues: {_specKey: spec, StackZoneSpecification.disableKey: false}); |
+ } |
+ |
+ /// If [when] is `true` and this is called within a [Chain.capture] zone, runs |
+ /// [callback] in a [Zone] in which chain capturing is disabled. |
+ /// |
+ /// If [callback] returns a value, it will be returned by [disable] as well. |
+ static/*=T*/ disable/*<T>*/(/*=T*/ callback(), {bool when: true}) { |
+ var zoneValues = |
+ when ? {_specKey: null, StackZoneSpecification.disableKey: true} : null; |
+ |
+ return runZoned(callback, zoneValues: zoneValues); |
} |
/// Returns [futureOrStream] unmodified. |
@@ -94,9 +127,18 @@ class Chain implements StackTrace { |
/// |
/// If this is called outside of a [capture] zone, it just returns a |
/// single-trace chain. |
- factory Chain.current([int level=0]) { |
+ factory Chain.current([int level = 0]) { |
if (_currentSpec != null) return _currentSpec.currentChain(level + 1); |
- return new Chain([new Trace.current(level + 1)]); |
+ |
+ var chain = new Chain.forTrace(StackTrace.current); |
+ return new LazyChain(() { |
+ // JS includes a frame for the call to StackTrace.current, but the VM |
+ // doesn't, so we skip an extra frame in a JS context. |
+ var first = new Trace( |
+ chain.traces.first.frames.skip(level + (inJS ? 2 : 1)), |
+ original: chain.traces.first.original.toString()); |
+ return new Chain([first]..addAll(chain.traces.skip(1))); |
+ }); |
} |
/// Returns the stack chain associated with [trace]. |
@@ -109,8 +151,8 @@ class Chain implements StackTrace { |
/// 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); |
+ if (_currentSpec != null) return _currentSpec.chainFor(trace); |
+ return new LazyChain(() => new Chain.parse(trace.toString())); |
} |
/// Parses a string representation of a stack chain. |
@@ -120,6 +162,10 @@ class Chain implements StackTrace { |
/// and returned as a single-trace chain. |
factory Chain.parse(String chain) { |
if (chain.isEmpty) return new Chain([]); |
+ if (chain.contains(vmChainGap)) { |
+ return new Chain( |
+ chain.split(vmChainGap).map((trace) => new Trace.parseVM(trace))); |
+ } |
if (!chain.contains(chainGap)) return new Chain([new Trace.parse(chain)]); |
return new Chain( |
@@ -127,13 +173,19 @@ class Chain implements StackTrace { |
} |
/// Returns a new [Chain] comprised of [traces]. |
- Chain(Iterable<Trace> traces) |
- : traces = new UnmodifiableListView<Trace>(traces.toList()); |
+ Chain(Iterable<Trace> traces) : traces = new List<Trace>.unmodifiable(traces); |
/// Returns a terser version of [this]. |
/// |
/// This calls [Trace.terse] on every trace in [traces], and discards any |
/// trace that contain only internal frames. |
+ /// |
+ /// This won't do anything with a raw JavaScript trace, since there's no way |
+ /// to determine which frames come from which Dart libraries. However, the |
+ /// [`source_map_stack_trace`][source_map_stack_trace] package can be used to |
+ /// convert JavaScript traces into Dart-style traces. |
+ /// |
+ /// [source_map_stack_trace]: https://pub.dartlang.org/packages/source_map_stack_trace |
Chain get terse => foldFrames((_) => false, terse: true); |
/// Returns a new [Chain] based on [this] where multiple stack frames matching |
@@ -150,11 +202,12 @@ class Chain implements StackTrace { |
/// library or from this package, and simplify core library frames as in |
/// [Trace.terse]. |
Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) { |
- var foldedTraces = traces.map( |
- (trace) => trace.foldFrames(predicate, terse: terse)); |
+ var foldedTraces = |
+ traces.map((trace) => trace.foldFrames(predicate, terse: terse)); |
var nonEmptyTraces = foldedTraces.where((trace) { |
// Ignore traces that contain only folded frames. |
if (trace.frames.length > 1) return true; |
+ if (trace.frames.isEmpty) return false; |
// In terse mode, the trace may have removed an outer folded frame, |
// leaving a single non-folded frame. We can detect a folded frame because |
@@ -176,12 +229,13 @@ class Chain implements StackTrace { |
/// |
/// 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))); |
+ Trace toTrace() => new Trace(traces.expand((trace) => trace.frames)); |
String toString() { |
// Figure out the longest path so we know how much to pad. |
var longest = traces.map((trace) { |
- return trace.frames.map((frame) => frame.location.length) |
+ return trace.frames |
+ .map((frame) => frame.location.length) |
.fold(0, math.max); |
}).fold(0, math.max); |
@@ -189,7 +243,7 @@ class Chain implements StackTrace { |
// padding is consistent across all traces. |
return traces.map((trace) { |
return trace.frames.map((frame) { |
- return '${padRight(frame.location, longest)} ${frame.member}\n'; |
+ return '${frame.location.padRight(longest)} ${frame.member}\n'; |
}).join(); |
}).join(chainGap); |
} |