Add a "terse" argument to foldFrames().
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//917423002
diff --git a/pkgs/stack_trace/CHANGELOG.md b/pkgs/stack_trace/CHANGELOG.md
index edd8ba6..47bd9b1 100644
--- a/pkgs/stack_trace/CHANGELOG.md
+++ b/pkgs/stack_trace/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.2.0
+
+* Add a `terse` argument to `Trace.foldFrames()` and `Chain.foldFrames()`. This
+ allows them to inherit the behavior of `Trace.terse` and `Chain.terse` without
+ having to duplicate the logic.
+
## 1.1.3
* Produce nicer-looking stack chains when using the VM's async/await
diff --git a/pkgs/stack_trace/lib/src/chain.dart b/pkgs/stack_trace/lib/src/chain.dart
index f027025..99726f3 100644
--- a/pkgs/stack_trace/lib/src/chain.dart
+++ b/pkgs/stack_trace/lib/src/chain.dart
@@ -136,21 +136,7 @@
///
/// This calls [Trace.terse] on every trace in [traces], and discards any
/// trace that contain only internal frames.
- Chain get terse {
- var terseTraces = traces.map((trace) => trace.terse);
- var nonEmptyTraces = terseTraces.where((trace) {
- // Ignore traces that contain only internal processing.
- return trace.frames.length > 1;
- });
-
- // If all the traces contain only internal processing, preserve the last
- // (top-most) one so that the chain isn't empty.
- if (nonEmptyTraces.isEmpty && terseTraces.isNotEmpty) {
- return new Chain([terseTraces.last]);
- }
-
- return new Chain(nonEmptyTraces);
- }
+ Chain get terse => foldFrames((_) => false, terse: true);
/// Returns a new [Chain] based on [this] where multiple stack frames matching
/// [predicate] are folded together.
@@ -161,8 +147,13 @@
///
/// 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.
- Chain foldFrames(bool predicate(Frame frame)) {
- var foldedTraces = traces.map((trace) => trace.foldFrames(predicate));
+ ///
+ /// If [terse] is true, this will also fold together frames from the core
+ /// 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 nonEmptyTraces = foldedTraces.where((trace) {
// Ignore traces that contain only folded frames. These traces will be
// folded into a single frame each.
diff --git a/pkgs/stack_trace/lib/src/trace.dart b/pkgs/stack_trace/lib/src/trace.dart
index 5cd09e9..53dbe33 100644
--- a/pkgs/stack_trace/lib/src/trace.dart
+++ b/pkgs/stack_trace/lib/src/trace.dart
@@ -203,36 +203,43 @@
/// core library or from this package, as in [foldFrames]. Remaining core
/// library frames have their libraries, "-patch" suffixes, and line numbers
/// removed.
- Trace get terse {
- return new Trace(foldFrames((frame) {
- 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;
- }).frames.map((frame) {
- if (!frame.isCore) return frame;
- var library = frame.library.replaceAll(_terseRegExp, '');
- return new Frame(Uri.parse(library), null, null, frame.member);
- }));
- }
+ ///
+ /// For custom folding, see [foldFrames].
+ Trace get terse => foldFrames((_) => false, terse: true);
/// 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.
+ /// [predicate] are folded together.
///
- /// 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.
- Trace foldFrames(bool predicate(Frame frame)) {
- var newFrames = <Frame>[];
+ /// 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, and simplify core library frames as in
+ /// [Trace.terse].
+ Trace foldFrames(bool predicate(Frame frame), {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 = [];
for (var frame in frames.reversed) {
if (!predicate(frame)) {
newFrames.add(frame);
@@ -242,6 +249,14 @@
}
}
+ if (terse) {
+ newFrames = newFrames.map((frame) {
+ if (!frame.isCore) return frame;
+ var library = frame.library.replaceAll(_terseRegExp, '');
+ return new Frame(Uri.parse(library), null, null, frame.member);
+ }).toList();
+ }
+
return new Trace(newFrames.reversed);
}
diff --git a/pkgs/stack_trace/pubspec.yaml b/pkgs/stack_trace/pubspec.yaml
index ded96f6..01b1550 100644
--- a/pkgs/stack_trace/pubspec.yaml
+++ b/pkgs/stack_trace/pubspec.yaml
@@ -7,7 +7,7 @@
#
# When the major version is upgraded, you *must* update that version constraint
# in pub to stay in sync with this.
-version: 1.1.3
+version: 1.2.0
author: "Dart Team <misc@dartlang.org>"
homepage: http://github.com/dart-lang/stack_trace
description: >
diff --git a/pkgs/stack_trace/test/chain_test.dart b/pkgs/stack_trace/test/chain_test.dart
index 6af3c6b..54bc5f5 100644
--- a/pkgs/stack_trace/test/chain_test.dart
+++ b/pkgs/stack_trace/test/chain_test.dart
@@ -572,6 +572,33 @@
'b.dart 10:11 Zop.zoop\n'));
});
+ test('with terse: true, folds core frames as well', () {
+ var chain = new Chain([
+ new Trace.parse(
+ 'a.dart 10:11 Foo.bar\n'
+ 'dart:async-patch/future.dart 10:11 Zip.zap\n'
+ 'b.dart 10:11 Bang.qux\n'
+ 'dart:core 10:11 Bar.baz\n'
+ 'a.dart 10:11 Zop.zoop'),
+ new Trace.parse(
+ 'a.dart 10:11 Foo.bar\n'
+ 'a.dart 10:11 Bar.baz\n'
+ 'a.dart 10:11 Bang.qux\n'
+ 'a.dart 10:11 Zip.zap\n'
+ 'b.dart 10:11 Zop.zoop')
+ ]);
+
+ var folded = chain.foldFrames((frame) => frame.library == 'a.dart',
+ terse: true);
+ expect(folded.toString(), equals(
+ 'dart:async Zip.zap\n'
+ 'b.dart 10:11 Bang.qux\n'
+ 'a.dart 10:11 Zop.zoop\n'
+ '===== asynchronous gap ===========================\n'
+ 'a.dart 10:11 Zip.zap\n'
+ 'b.dart 10:11 Zop.zoop\n'));
+ });
+
test('eliminates completely-folded traces', () {
var chain = new Chain([
new Trace.parse(
diff --git a/pkgs/stack_trace/test/trace_test.dart b/pkgs/stack_trace/test/trace_test.dart
index c8a848b..125ba95 100644
--- a/pkgs/stack_trace/test/trace_test.dart
+++ b/pkgs/stack_trace/test/trace_test.dart
@@ -315,4 +315,24 @@
dart:async-patch/future.dart 9:11 fooBottom
'''));
});
+
+ test('.foldFrames with terse: true, folds core frames as well', () {
+ var trace = new Trace.parse('''
+#0 notFoo (foo.dart:42:21)
+#1 fooTop (bar.dart:0:2)
+#2 coreBottom (dart:async/future.dart:0:2)
+#3 alsoNotFoo (bar.dart:10:20)
+#4 fooTop (foo.dart:9:11)
+#5 coreBottom (dart:async-patch/future.dart:9:11)
+''');
+
+ var folded = trace.foldFrames((frame) => frame.member.startsWith('foo'),
+ terse: true);
+ expect(folded.toString(), equals('''
+foo.dart 42:21 notFoo
+dart:async coreBottom
+bar.dart 10:20 alsoNotFoo
+dart:async coreBottom
+'''));
+ });
}