Chain.forTrace() trims the innermost trace's VM chain This was already being trimmed for all other stack traces, but the innermost one requires different logic, so we missed it.
diff --git a/pkgs/stack_trace/CHANGELOG.md b/pkgs/stack_trace/CHANGELOG.md index 916448c..b0c972a 100644 --- a/pkgs/stack_trace/CHANGELOG.md +++ b/pkgs/stack_trace/CHANGELOG.md
@@ -8,6 +8,11 @@ synchronously within `Chain.capture()`. This matches the existing behavior outside `Chain.capture()`. +* `Chain.forTrace()` now trims the VM's stack chains for the innermost stack + trace within `Chain.capture()` (unless it's called synchronously, as above). + This avoids duplicated frames and makes the format of the innermost traces + consistent with the other traces in the chain. + ## 1.8.2 * Update to use strong-mode clean Zone API.
diff --git a/pkgs/stack_trace/lib/src/stack_zone_specification.dart b/pkgs/stack_trace/lib/src/stack_zone_specification.dart index 2db98b7..cd5d068 100644 --- a/pkgs/stack_trace/lib/src/stack_zone_specification.dart +++ b/pkgs/stack_trace/lib/src/stack_zone_specification.dart
@@ -90,13 +90,20 @@ trace ??= StackTrace.current; var previous = _chains[trace] ?? _currentNode; - if (previous != null) return new _Node(trace, previous).toChain(); + if (previous == null) { + // If there's no [_currentNode], we're running synchronously beneath + // [Chain.capture] and we should fall back to the VM's stack chaining. We + // can't use [Chain.from] here because it'll just call [chainFor] again. + if (trace is Trace) return new Chain([trace]); + return new LazyChain(() => new Chain.parse(trace.toString())); + } else { + if (trace is! Trace) { + var original = trace; + trace = new LazyTrace(() => new Trace.parse(_trimVMChain(original))); + } - // If there's no [_currentNode], we're running synchronously beneath - // [Chain.capture] and we should fall back to the VM's stack chaining. We - // can't use [Chain.from] here because it'll just call [chainFor] again. - if (trace is Trace) return new Chain([trace]); - return new LazyChain(() => new Chain.parse(trace.toString())); + return new _Node(trace, previous).toChain(); + } } /// Tracks the current stack chain so it can be set to [_currentChain] when @@ -203,6 +210,29 @@ _currentNode = previousNode; } } + + /// 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(() { + var text = _trimVMChain(stackTrace); + 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); + }); + } + + /// Removes the VM's stack chains from the native [trace], since we're + /// generating our own and we don't want duplicate frames. + String _trimVMChain(StackTrace trace) { + var text = trace.toString(); + var index = text.indexOf(vmChainGap); + return index == -1 ? text : text.substring(0, index); + } } /// A linked list node representing a single entry in a stack chain. @@ -213,8 +243,7 @@ /// The previous node in the chain. final _Node previous; - _Node(StackTrace trace, [this.previous]) - : trace = trace == null ? _currentTrace() : new Trace.from(trace); + _Node(StackTrace trace, [this.previous]) : trace = new Trace.from(trace); /// Converts this to a [Chain]. Chain toChain() { @@ -227,22 +256,3 @@ 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); - }); -}
diff --git a/pkgs/stack_trace/pubspec.yaml b/pkgs/stack_trace/pubspec.yaml index 79cf0b0..65d3ef6 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.8.3-dev +version: 1.8.3 author: "Dart Team <misc@dartlang.org>" homepage: https://github.com/dart-lang/stack_trace description: A package for manipulating stack traces and printing them readably.
diff --git a/pkgs/stack_trace/test/chain/vm_test.dart b/pkgs/stack_trace/test/chain/vm_test.dart index 6f793c4..a47cbf7 100644 --- a/pkgs/stack_trace/test/chain/vm_test.dart +++ b/pkgs/stack_trace/test/chain/vm_test.dart
@@ -468,8 +468,11 @@ }); expect(chain.traces, hasLength(2)); + + // Assert that we've trimmed the VM's stack chains here to avoid + // duplication. expect(chain.traces.first.toString(), - equals(new Trace.from(trace).toString())); + equals(new Chain.parse(trace.toString()).traces.first.toString())); expect( chain.traces.last.frames, contains(frameMember(startsWith('main')))); });