Index: packages/stack_trace/lib/src/stack_zone_specification.dart |
diff --git a/packages/stack_trace/lib/src/stack_zone_specification.dart b/packages/stack_trace/lib/src/stack_zone_specification.dart |
index 25d96427110f76a22b134fbaa2634025225434cd..eb06beb32f99b1e71d60880cfec6cea70e2b32f2 100644 |
--- a/packages/stack_trace/lib/src/stack_zone_specification.dart |
+++ b/packages/stack_trace/lib/src/stack_zone_specification.dart |
@@ -2,12 +2,15 @@ |
// 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.stack_zone_specification; |
- |
import 'dart:async'; |
-import 'trace.dart'; |
import 'chain.dart'; |
+import 'lazy_trace.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 class encapsulating the zone specification for a [Chain.capture] zone. |
/// |
@@ -29,6 +32,15 @@ import 'chain.dart'; |
/// Since [ZoneSpecification] can't be extended or even implemented, in order to |
/// get a real [ZoneSpecification] instance it's necessary to call [toSpec]. |
class StackZoneSpecification { |
+ /// An opaque object used as a zone value to disable chain tracking in a given |
+ /// zone. |
+ /// |
+ /// If `Zone.current[disableKey]` is `true`, no stack chains will be tracked. |
+ static final disableKey = new Object(); |
+ |
+ /// Whether chain-tracking is disabled in the current zone. |
+ bool get _disabled => Zone.current[disableKey] == true; |
+ |
/// The expando that associates stack chains with [StackTrace]s. |
/// |
/// The chains are associated with stack traces rather than errors themselves |
@@ -43,7 +55,7 @@ class StackZoneSpecification { |
/// |
/// If this is null, that indicates that any unhandled errors should be passed |
/// to the parent zone. |
- final ChainHandler _onError; |
+ final _ChainHandler _onError; |
/// The most recent node of the current stack chain. |
_Node _currentNode; |
@@ -53,11 +65,11 @@ class StackZoneSpecification { |
/// Converts [this] to a real [ZoneSpecification]. |
ZoneSpecification toSpec() { |
return new ZoneSpecification( |
- handleUncaughtError: handleUncaughtError, |
- registerCallback: registerCallback, |
- registerUnaryCallback: registerUnaryCallback, |
- registerBinaryCallback: registerBinaryCallback, |
- errorCallback: errorCallback); |
+ handleUncaughtError: _handleUncaughtError, |
+ registerCallback: _registerCallback, |
+ registerUnaryCallback: _registerUnaryCallback, |
+ registerBinaryCallback: _registerBinaryCallback, |
+ errorCallback: _errorCallback); |
} |
/// Returns the current stack chain. |
@@ -65,7 +77,7 @@ class StackZoneSpecification { |
/// By default, the first frame of the first trace will be the line where |
/// [currentChain] is called. If [level] is passed, the first trace will start |
/// that many frames up instead. |
- Chain currentChain([int level=0]) => _createNode(level + 1).toChain(); |
+ Chain currentChain([int level = 0]) => _createNode(level + 1).toChain(); |
/// Returns the stack chain associated with [trace], if one exists. |
/// |
@@ -78,57 +90,20 @@ class StackZoneSpecification { |
return new _Node(trace, previous).toChain(); |
} |
- /// Ensures that an error emitted by [future] has the correct stack |
- /// information associated with it. |
- /// |
- /// By default, the first frame of the first trace will be the line where |
- /// [trackFuture] is called. If [level] is passed, the first trace will start |
- /// that many frames up instead. |
- Future trackFuture(Future future, [int level=0]) { |
- var completer = new Completer.sync(); |
- var node = _createNode(level + 1); |
- future.then(completer.complete).catchError((e, stackTrace) { |
- if (stackTrace == null) stackTrace = new Trace.current(); |
- if (stackTrace is! Chain && _chains[stackTrace] == null) { |
- _chains[stackTrace] = node; |
- } |
- completer.completeError(e, stackTrace); |
- }); |
- return completer.future; |
- } |
- |
- /// Ensures that any errors emitted by [stream] have the correct stack |
- /// information associated with them. |
- /// |
- /// By default, the first frame of the first trace will be the line where |
- /// [trackStream] is called. If [level] is passed, the first trace will start |
- /// that many frames up instead. |
- Stream trackStream(Stream stream, [int level=0]) { |
- var node = _createNode(level + 1); |
- return stream.transform(new StreamTransformer.fromHandlers( |
- handleError: (error, stackTrace, sink) { |
- if (stackTrace == null) stackTrace = new Trace.current(); |
- if (stackTrace is! Chain && _chains[stackTrace] == null) { |
- _chains[stackTrace] = node; |
- } |
- sink.addError(error, stackTrace); |
- })); |
- } |
- |
/// Tracks the current stack chain so it can be set to [_currentChain] when |
/// [f] is run. |
- ZoneCallback registerCallback(Zone self, ZoneDelegate parent, Zone zone, |
- Function f) { |
- if (f == null) return parent.registerCallback(zone, null); |
+ ZoneCallback _registerCallback( |
+ Zone self, ZoneDelegate parent, Zone zone, Function f) { |
+ if (f == null || _disabled) return parent.registerCallback(zone, f); |
var node = _createNode(1); |
return parent.registerCallback(zone, () => _run(f, node)); |
} |
/// Tracks the current stack chain so it can be set to [_currentChain] when |
/// [f] is run. |
- ZoneUnaryCallback registerUnaryCallback(Zone self, ZoneDelegate parent, |
- Zone zone, Function f) { |
- if (f == null) return parent.registerUnaryCallback(zone, null); |
+ ZoneUnaryCallback _registerUnaryCallback( |
+ Zone self, ZoneDelegate parent, Zone zone, Function f) { |
+ if (f == null || _disabled) return parent.registerUnaryCallback(zone, f); |
var node = _createNode(1); |
return parent.registerUnaryCallback(zone, (arg) { |
return _run(() => f(arg), node); |
@@ -137,9 +112,10 @@ class StackZoneSpecification { |
/// Tracks the current stack chain so it can be set to [_currentChain] when |
/// [f] is run. |
- ZoneBinaryCallback registerBinaryCallback(Zone self, ZoneDelegate parent, |
- Zone zone, Function f) { |
- if (f == null) return parent.registerBinaryCallback(zone, null); |
+ ZoneBinaryCallback _registerBinaryCallback( |
+ Zone self, ZoneDelegate parent, Zone zone, Function f) { |
+ if (f == null || _disabled) return parent.registerBinaryCallback(zone, f); |
+ |
var node = _createNode(1); |
return parent.registerBinaryCallback(zone, (arg1, arg2) { |
return _run(() => f(arg1, arg2), node); |
@@ -148,8 +124,12 @@ class StackZoneSpecification { |
/// Looks up the chain associated with [stackTrace] and passes it either to |
/// [_onError] or [parent]'s error handler. |
- handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, error, |
- StackTrace stackTrace) { |
+ _handleUncaughtError( |
+ Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace) { |
+ if (_disabled) { |
+ return parent.handleUncaughtError(zone, error, stackTrace); |
+ } |
+ |
var stackChain = chainFor(stackTrace); |
if (_onError == null) { |
return parent.handleUncaughtError(zone, error, stackChain); |
@@ -158,7 +138,7 @@ class StackZoneSpecification { |
// TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just |
// allow [runBinary] to throw instead once issue 18134 is fixed. |
try { |
- return parent.runBinary(zone, _onError, error, stackChain); |
+ return self.parent.runBinary(_onError, error, stackChain); |
} catch (newError, newStackTrace) { |
if (identical(newError, error)) { |
return parent.handleUncaughtError(zone, error, stackChain); |
@@ -170,8 +150,10 @@ class StackZoneSpecification { |
/// Attaches the current stack chain to [stackTrace], replacing it if |
/// necessary. |
- AsyncError errorCallback(Zone self, ZoneDelegate parent, Zone zone, |
+ AsyncError _errorCallback(Zone self, ZoneDelegate parent, Zone zone, |
Object error, StackTrace stackTrace) { |
+ if (_disabled) return parent.errorCallback(zone, error, stackTrace); |
+ |
// Go up two levels to get through [_CustomZone.errorCallback]. |
if (stackTrace == null) { |
stackTrace = _createNode(2).toChain(); |
@@ -189,8 +171,8 @@ class StackZoneSpecification { |
/// By default, the first frame of the first trace will be the line where |
/// [_createNode] is called. If [level] is passed, the first trace will start |
/// that many frames up instead. |
- _Node _createNode([int level=0]) => |
- new _Node(new Trace.current(level + 1), _currentNode); |
+ _Node _createNode([int level = 0]) => |
+ new _Node(_currentTrace(level + 1), _currentNode); |
// TODO(nweiz): use a more robust way of detecting and tracking errors when |
// issue 15105 is fixed. |
@@ -221,7 +203,7 @@ class _Node { |
final _Node previous; |
_Node(StackTrace trace, [this.previous]) |
- : trace = trace == null ? new Trace.current() : new Trace.from(trace); |
+ : trace = trace == null ? _currentTrace() : new Trace.from(trace); |
/// Converts this to a [Chain]. |
Chain toChain() { |
@@ -234,3 +216,22 @@ class _Node { |
return new Chain(nodes); |
} |
} |
+ |
+/// Like [new Trace.current], but if the current stack trace has VM chaining |
+/// enabled, this only returns the innermost sub-trace. |
+Trace _currentTrace([int level]) { |
+ level ??= 0; |
+ var stackTrace = StackTrace.current; |
+ return new LazyTrace(() { |
+ // Ignore the VM's stack chains when we generate our own. Otherwise we'll |
+ // end up with duplicate frames all over the place. |
+ var text = stackTrace.toString(); |
+ var index = text.indexOf(vmChainGap); |
+ if (index != -1) text = text.substring(0, index); |
+ |
+ var trace = new Trace.parse(text); |
+ // 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. |
+ return new Trace(trace.frames.skip(level + (inJS ? 2 : 1)), original: text); |
+ }); |
+} |