LCOV - code coverage report
Current view: top level - stack_trace-1.8.2/lib/src - stack_zone_specification.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 26 54 48.1 %
Date: 2017-10-10 20:17:03 Functions: 0 0 -

          Line data    Source code
       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             : import 'dart:async';
       6             : 
       7             : import 'chain.dart';
       8             : import 'lazy_trace.dart';
       9             : import 'trace.dart';
      10             : import 'utils.dart';
      11             : 
      12             : /// A function that handles errors in the zone wrapped by [Chain.capture].
      13             : typedef void _ChainHandler(error, Chain chain);
      14             : 
      15             : /// A class encapsulating the zone specification for a [Chain.capture] zone.
      16             : ///
      17             : /// Until they're materialized and exposed to the user, stack chains are tracked
      18             : /// as linked lists of [Trace]s using the [_Node] class. These nodes are stored
      19             : /// in three distinct ways:
      20             : ///
      21             : /// * When a callback is registered, a node is created and stored as a captured
      22             : ///   local variable until the callback is run.
      23             : ///
      24             : /// * When a callback is run, its captured node is set as the [_currentNode] so
      25             : ///   it can be available to [Chain.current] and to be linked into additional
      26             : ///   chains when more callbacks are scheduled.
      27             : ///
      28             : /// * When a callback throws an error or a Future or Stream emits an error, the
      29             : ///   current node is associated with that error's stack trace using the
      30             : ///   [_chains] expando.
      31             : ///
      32             : /// Since [ZoneSpecification] can't be extended or even implemented, in order to
      33             : /// get a real [ZoneSpecification] instance it's necessary to call [toSpec].
      34             : class StackZoneSpecification {
      35             :   /// An opaque object used as a zone value to disable chain tracking in a given
      36             :   /// zone.
      37             :   ///
      38             :   /// If `Zone.current[disableKey]` is `true`, no stack chains will be tracked.
      39             :   static final disableKey = new Object();
      40             : 
      41             :   /// Whether chain-tracking is disabled in the current zone.
      42          20 :   bool get _disabled => Zone.current[disableKey] == true;
      43             : 
      44             :   /// The expando that associates stack chains with [StackTrace]s.
      45             :   ///
      46             :   /// The chains are associated with stack traces rather than errors themselves
      47             :   /// because it's a common practice to throw strings as errors, which can't be
      48             :   /// used with expandos.
      49             :   ///
      50             :   /// The chain associated with a given stack trace doesn't contain a node for
      51             :   /// that stack trace.
      52             :   final _chains = new Expando<_Node>("stack chains");
      53             : 
      54             :   /// The error handler for the zone.
      55             :   ///
      56             :   /// If this is null, that indicates that any unhandled errors should be passed
      57             :   /// to the parent zone.
      58             :   final _ChainHandler _onError;
      59             : 
      60             :   /// The most recent node of the current stack chain.
      61             :   _Node _currentNode;
      62             : 
      63           5 :   StackZoneSpecification([this._onError]);
      64             : 
      65             :   /// Converts [this] to a real [ZoneSpecification].
      66             :   ZoneSpecification toSpec() {
      67           5 :     return new ZoneSpecification(
      68           5 :         handleUncaughtError: _handleUncaughtError,
      69           5 :         registerCallback: _registerCallback,
      70           5 :         registerUnaryCallback: _registerUnaryCallback,
      71           5 :         registerBinaryCallback: _registerBinaryCallback,
      72           5 :         errorCallback: _errorCallback);
      73             :   }
      74             : 
      75             :   /// Returns the current stack chain.
      76             :   ///
      77             :   /// By default, the first frame of the first trace will be the line where
      78             :   /// [currentChain] is called. If [level] is passed, the first trace will start
      79             :   /// that many frames up instead.
      80           0 :   Chain currentChain([int level = 0]) => _createNode(level + 1).toChain();
      81             : 
      82             :   /// Returns the stack chain associated with [trace], if one exists.
      83             :   ///
      84             :   /// The first stack trace in the returned chain will always be [trace]
      85             :   /// (converted to a [Trace] if necessary). If there is no chain associated
      86             :   /// with [trace], this just returns a single-trace chain containing [trace].
      87             :   Chain chainFor(StackTrace trace) {
      88           0 :     if (trace is Chain) return trace;
      89           0 :     var previous = trace == null ? null : _chains[trace];
      90           0 :     return new _Node(trace, previous).toChain();
      91             :   }
      92             : 
      93             :   /// Tracks the current stack chain so it can be set to [_currentChain] when
      94             :   /// [f] is run.
      95             :   ZoneCallback<R> _registerCallback<R>(
      96             :       Zone self, ZoneDelegate parent, Zone zone, R f()) {
      97           5 :     if (f == null || _disabled) return parent.registerCallback(zone, f);
      98           5 :     var node = _createNode(1);
      99          10 :     return parent.registerCallback(zone, () => _run(f, node));
     100             :   }
     101             : 
     102             :   /// Tracks the current stack chain so it can be set to [_currentChain] when
     103             :   /// [f] is run.
     104             :   ZoneUnaryCallback<R, T> _registerUnaryCallback<R, T>(
     105             :       Zone self, ZoneDelegate parent, Zone zone, R f(T arg)) {
     106           5 :     if (f == null || _disabled) return parent.registerUnaryCallback(zone, f);
     107           5 :     var node = _createNode(1);
     108           5 :     return parent.registerUnaryCallback(zone, (arg) {
     109          10 :       return _run(() => f(arg), node);
     110             :     });
     111             :   }
     112             : 
     113             :   /// Tracks the current stack chain so it can be set to [_currentChain] when
     114             :   /// [f] is run.
     115             :   ZoneBinaryCallback<R, T1, T2> _registerBinaryCallback<R, T1, T2>(
     116             :       Zone self, ZoneDelegate parent, Zone zone, Function f) {
     117           5 :     if (f == null || _disabled) return parent.registerBinaryCallback(zone, f);
     118             : 
     119           5 :     var node = _createNode(1);
     120           5 :     return parent.registerBinaryCallback(zone, (arg1, arg2) {
     121           0 :       return _run(() => f(arg1, arg2), node);
     122             :     });
     123             :   }
     124             : 
     125             :   /// Looks up the chain associated with [stackTrace] and passes it either to
     126             :   /// [_onError] or [parent]'s error handler.
     127             :   void _handleUncaughtError(
     128             :       Zone self, ZoneDelegate parent, Zone zone, error, StackTrace stackTrace) {
     129           0 :     if (_disabled) {
     130           0 :       parent.handleUncaughtError(zone, error, stackTrace);
     131             :       return;
     132             :     }
     133             : 
     134           0 :     var stackChain = chainFor(stackTrace);
     135           0 :     if (_onError == null) {
     136           0 :       parent.handleUncaughtError(zone, error, stackChain);
     137             :       return;
     138             :     }
     139             : 
     140             :     // TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just
     141             :     // allow [runBinary] to throw instead once issue 18134 is fixed.
     142             :     try {
     143           0 :       self.parent.runBinary(_onError, error, stackChain);
     144             :     } catch (newError, newStackTrace) {
     145             :       if (identical(newError, error)) {
     146           0 :         parent.handleUncaughtError(zone, error, stackChain);
     147             :       } else {
     148           0 :         parent.handleUncaughtError(zone, newError, newStackTrace);
     149             :       }
     150             :     }
     151             :   }
     152             : 
     153             :   /// Attaches the current stack chain to [stackTrace], replacing it if
     154             :   /// necessary.
     155             :   AsyncError _errorCallback(Zone self, ZoneDelegate parent, Zone zone,
     156             :       Object error, StackTrace stackTrace) {
     157           0 :     if (_disabled) return parent.errorCallback(zone, error, stackTrace);
     158             : 
     159             :     // Go up two levels to get through [_CustomZone.errorCallback].
     160             :     if (stackTrace == null) {
     161           0 :       stackTrace = _createNode(2).toChain();
     162             :     } else {
     163           0 :       if (_chains[stackTrace] == null) _chains[stackTrace] = _createNode(2);
     164             :     }
     165             : 
     166           0 :     var asyncError = parent.errorCallback(zone, error, stackTrace);
     167           0 :     return asyncError == null ? new AsyncError(error, stackTrace) : asyncError;
     168             :   }
     169             : 
     170             :   /// Creates a [_Node] with the current stack trace and linked to
     171             :   /// [_currentNode].
     172             :   ///
     173             :   /// By default, the first frame of the first trace will be the line where
     174             :   /// [_createNode] is called. If [level] is passed, the first trace will start
     175             :   /// that many frames up instead.
     176             :   _Node _createNode([int level = 0]) =>
     177          20 :       new _Node(_currentTrace(level + 1), _currentNode);
     178             : 
     179             :   // TODO(nweiz): use a more robust way of detecting and tracking errors when
     180             :   // issue 15105 is fixed.
     181             :   /// Runs [f] with [_currentNode] set to [node].
     182             :   ///
     183             :   /// If [f] throws an error, this associates [node] with that error's stack
     184             :   /// trace.
     185             :   T _run<T>(T f(), _Node node) {
     186           5 :     var previousNode = _currentNode;
     187           5 :     _currentNode = node;
     188             :     try {
     189           5 :       return f();
     190             :     } catch (e, stackTrace) {
     191           0 :       _chains[stackTrace] = node;
     192             :       rethrow;
     193             :     } finally {
     194           5 :       _currentNode = previousNode;
     195             :     }
     196             :   }
     197             : }
     198             : 
     199             : /// A linked list node representing a single entry in a stack chain.
     200             : class _Node {
     201             :   /// The stack trace for this link of the chain.
     202             :   final Trace trace;
     203             : 
     204             :   /// The previous node in the chain.
     205             :   final _Node previous;
     206             : 
     207             :   _Node(StackTrace trace, [this.previous])
     208          10 :       : trace = trace == null ? _currentTrace() : new Trace.from(trace);
     209             : 
     210             :   /// Converts this to a [Chain].
     211             :   Chain toChain() {
     212           0 :     var nodes = <Trace>[];
     213             :     var node = this;
     214             :     while (node != null) {
     215           0 :       nodes.add(node.trace);
     216           0 :       node = node.previous;
     217             :     }
     218           0 :     return new Chain(nodes);
     219             :   }
     220             : }
     221             : 
     222             : /// Like [new Trace.current], but if the current stack trace has VM chaining
     223             : /// enabled, this only returns the innermost sub-trace.
     224             : Trace _currentTrace([int level]) {
     225             :   level ??= 0;
     226           5 :   var stackTrace = StackTrace.current;
     227           5 :   return new LazyTrace(() {
     228             :     // Ignore the VM's stack chains when we generate our own. Otherwise we'll
     229             :     // end up with duplicate frames all over the place.
     230           0 :     var text = stackTrace.toString();
     231           0 :     var index = text.indexOf(vmChainGap);
     232           0 :     if (index != -1) text = text.substring(0, index);
     233             : 
     234           0 :     var trace = new Trace.parse(text);
     235             :     // JS includes a frame for the call to StackTrace.current, but the VM
     236             :     // doesn't, so we skip an extra frame in a JS context.
     237           0 :     return new Trace(trace.frames.skip(level + (inJS ? 2 : 1)), original: text);
     238             :   });
     239             : }

Generated by: LCOV version 1.13