foldFrames method Null safety

Trace foldFrames(
  1. bool predicate(
    1. Frame
    ),
  2. {bool terse = false}
)

Returns a new Trace based on this where multiple stack frames matching predicate are folded together.

This means that whenever there are multiple frames in a row that match predicate, only the last one is kept. This is useful for limiting the amount of library code that appears in a stack trace by only showing user code and code that's called by user code.

If terse is true, this will also fold together frames from the core library or from this package, simplify core library frames, and potentially remove the outermost frame as in Trace.terse.

Implementation

Trace foldFrames(bool Function(Frame) predicate, {bool terse = false}) {
  if (terse) {
    var oldPredicate = predicate;
    predicate = (frame) {
      if (oldPredicate(frame)) return true;

      if (frame.isCore) return true;
      if (frame.package == 'stack_trace') return true;

      // Ignore async stack frames without any line or column information.
      // These come from the VM's async/await implementation and represent
      // internal frames. They only ever show up in stack chains and are
      // always surrounded by other traces that are actually useful, so we can
      // just get rid of them.
      // TODO(nweiz): Get rid of this logic some time after issue 22009 is
      // fixed.
      if (!frame.member!.contains('<async>')) return false;
      return frame.line == null;
    };
  }

  var newFrames = <Frame>[];
  for (var frame in frames.reversed) {
    if (frame is UnparsedFrame || !predicate(frame)) {
      newFrames.add(frame);
    } else if (newFrames.isEmpty || !predicate(newFrames.last)) {
      newFrames.add(Frame(frame.uri, frame.line, frame.column, frame.member));
    }
  }

  if (terse) {
    newFrames = newFrames.map((frame) {
      if (frame is UnparsedFrame || !predicate(frame)) return frame;
      var library = frame.library.replaceAll(_terseRegExp, '');
      return Frame(Uri.parse(library), null, null, frame.member);
    }).toList();

    if (newFrames.length > 1 && predicate(newFrames.first)) {
      newFrames.removeAt(0);
    }
  }

  return Trace(newFrames.reversed, original: original.toString());
}