Revert "Elide tree walks (#48413)" (#49886)
This reverts commit 46ccd086745bb9ade53f2457f3bb91942b2af970.
diff --git a/packages/flutter/lib/src/foundation/assertions.dart b/packages/flutter/lib/src/foundation/assertions.dart
index 6f8c868..7381a7b 100644
--- a/packages/flutter/lib/src/foundation/assertions.dart
+++ b/packages/flutter/lib/src/foundation/assertions.dart
@@ -20,130 +20,6 @@
/// and other callbacks that collect information describing an error.
typedef InformationCollector = Iterable<DiagnosticsNode> Function();
-/// Partial information from a stack frame for stack filtering purposes.
-///
-/// See also:
-///
-/// * [SubStackFilter], which uses this class to compare against [StackFrame]s.
-@immutable
-class PartialStackFrame {
- /// Creates a new [PartialStackFrame] instance. All arguments are required and
- /// must not be null.
- const PartialStackFrame({
- @required this.package,
- @required this.className,
- @required this.method,
- }) : assert(className != null),
- assert(method != null),
- assert(package != null);
-
- /// An `<asynchronous suspension>` line in a stack trace.
- static const PartialStackFrame asynchronousSuspension = PartialStackFrame(
- package: '',
- className: '',
- method: 'asynchronous suspension',
- );
-
- /// The package to match, e.g. `package:flutter/src/foundation/assertions.dart`,
- /// or `dart:ui/window.dart`.
- final Pattern package;
-
- /// The class name for the method.
- ///
- /// On web, this is ignored, since class names are not available.
- ///
- /// On all platforms, top level methods should use the empty string.
- final String className;
-
- /// The method name for this frame line.
- ///
- /// On web, private methods are wrapped with `[]`.
- final String method;
-
- /// Tests whether the [StackFrame] matches the information in this
- /// [PartialStackFrame].
- bool matches(StackFrame stackFrame) {
- final String stackFramePackage = '${stackFrame.packageScheme}:${stackFrame.package}/${stackFrame.packagePath}';
- // Ideally this wouldn't be necessary.
- // TODO(dnfield): https://github.com/dart-lang/sdk/issues/40117
- if (kIsWeb) {
- return package.allMatches(stackFramePackage).isNotEmpty
- && stackFrame.method == (method.startsWith('_') ? '[$method]' : method);
- }
- return package.allMatches(stackFramePackage).isNotEmpty
- && stackFrame.method == method
- && stackFrame.className == className;
- }
-}
-
-/// A class that filters stack frames for additional filtering on
-/// [FlutterError.defaultStackFilter].
-abstract class StackFilter {
- /// A const constructor to allow subclasses to be const.
- const StackFilter();
-
- /// Filters the list of [StackFrame]s by updating corrresponding indices in
- /// `reasons`.
- ///
- /// To elide a frame or number of frames, set the string
- void filter(List<StackFrame> stackFrames, List<String> reasons);
-}
-
-
-/// A [StackFilter] that filters based on repeating lists of
-/// [PartialStackFrame]s.
-///
-/// See also:
-///
-/// * [FlutterError.addDefaultStackFilter], a method to register additional
-/// stack filters for [FlutterError.defaultStackFilter].
-/// * [StackFrame], a class that can help with parsing stack frames.
-/// * [PartialStackFrame], a class that helps match partial method information
-/// to a stack frame.
-class RepetitiveStackFrameFilter extends StackFilter {
- /// Creates a new SubStackFilter. All parameters are required and must not be
- /// null.
- const RepetitiveStackFrameFilter({
- @required this.frames,
- @required this.replacement,
- }) : assert(frames != null),
- assert(replacement != null);
-
- /// The shape of this repetative stack pattern.
- final List<PartialStackFrame> frames;
-
- /// The number of frames in this pattern.
- int get numFrames => frames.length;
-
- /// The string to replace the frames with.
- ///
- /// If the same replacement string is used multiple times in a row, the
- /// [FlutterError.defaultStackFilter] will simply update a counter after this
- /// line rather than repeating it.
- final String replacement;
-
- List<String> get _replacements => List<String>.filled(numFrames, replacement);
-
- @override
- void filter(List<StackFrame> stackFrames, List<String> reasons) {
- for (int index = 0; index < stackFrames.length; index += 1) {
- if (_matchesFrames(stackFrames.skip(index).take(numFrames).toList())) {
- reasons.setRange(index, index + numFrames, _replacements);
- index += numFrames;
- }
- }
- }
-
- bool _matchesFrames(List<StackFrame> stackFrames) {
- for (int index = 0; index < stackFrames.length; index++) {
- if (!frames[index].matches(stackFrames[index])) {
- return false;
- }
- }
- return true;
- }
-}
-
abstract class _ErrorDiagnostic extends DiagnosticsProperty<List<Object>> {
/// This constructor provides a reliable hook for a kernel transformer to find
/// error messages that need to be rewritten to include object references for
@@ -777,19 +653,6 @@
_errorCount += 1;
}
- static final List<StackFilter> _stackFilters = <StackFilter>[];
-
- /// Adds a stack filtering function to [defaultStackFilter].
- ///
- /// For example, the framework adds common patterns of element building to
- /// elide tree-walking patterns in the stacktrace.
- ///
- /// Added filters are checked in order of addition. The first matching filter
- /// wins, and subsequent filters will not be checked.
- static void addDefaultStackFilter(StackFilter filter) {
- _stackFilters.add(filter);
- }
-
/// Converts a stack to a string that is more readable by omitting stack
/// frames that correspond to Dart internals.
///
@@ -802,76 +665,38 @@
/// format but the frame numbers will not be consecutive (frames are elided)
/// and the final line may be prose rather than a stack frame.
static Iterable<String> defaultStackFilter(Iterable<String> frames) {
- final Map<String, int> removedPackagesAndClasses = <String, int>{
- 'dart:async-patch': 0,
- 'dart:async': 0,
- 'package:stack_trace': 0,
- 'class _AssertionError': 0,
- 'class _FakeAsync': 0,
- 'class _FrameCallbackEntry': 0,
- 'class _Timer': 0,
- 'class _RawReceivePortImpl': 0,
+ const Set<String> filteredPackages = <String>{
+ 'dart:async-patch',
+ 'dart:async',
+ 'package:stack_trace',
};
- int skipped = 0;
-
- final List<StackFrame> parsedFrames = StackFrame.fromStackString(frames.join('\n'));
-
- for (int index = 0; index < parsedFrames.length; index += 1) {
- final StackFrame frame = parsedFrames[index];
- final String className = 'class ${frame.className}';
- final String package = '${frame.packageScheme}:${frame.package}';
- if (removedPackagesAndClasses.containsKey(className)) {
- skipped += 1;
- removedPackagesAndClasses[className] += 1;
- parsedFrames.removeAt(index);
- index -= 1;
- } else if (removedPackagesAndClasses.containsKey(package)) {
- skipped += 1;
- removedPackagesAndClasses[package] += 1;
- parsedFrames.removeAt(index);
- index -= 1;
- }
- }
- final List<String> reasons = List<String>(parsedFrames.length);
- for (final StackFilter filter in _stackFilters) {
- filter.filter(parsedFrames, reasons);
- }
-
+ const Set<String> filteredClasses = <String>{
+ '_AssertionError',
+ '_FakeAsync',
+ '_FrameCallbackEntry',
+ };
final List<String> result = <String>[];
-
- // Collapse duplicated reasons.
- for (int index = 0; index < parsedFrames.length; index += 1) {
- final int start = index;
- while (index < reasons.length - 1 && reasons[index] != null && reasons[index + 1] == reasons[index]) {
- index++;
+ final List<String> skipped = <String>[];
+ for (final String line in frames) {
+ final StackFrame frameLine = StackFrame.fromStackTraceLine(line);
+ if (filteredClasses.contains(frameLine.className)) {
+ skipped.add('class ${frameLine.className}');
+ } else if (filteredPackages.contains(frameLine.packageScheme + ':' + frameLine.package)) {
+ skipped.add('package ${frameLine.packageScheme == 'dart' ? 'dart:' : ''}${frameLine.package}');
+ } else {
+ result.add(line);
}
- String suffix = '';
- if (reasons[index] != null) {
- if (index != start) {
- suffix = ' (${index - start + 2} frames)';
- } else {
- suffix = ' (1 frame)';
- }
- }
- final String resultLine = '${reasons[index] ?? parsedFrames[index].source}$suffix';
- result.add(resultLine);
}
-
- // Only include packages we actually elided from.
- final List<String> where = <String>[
- for (MapEntry<String, int> entry in removedPackagesAndClasses.entries)
- if (entry.value > 0)
- entry.key
- ]..sort();
- if (skipped == 1) {
- result.add('(elided one frame from ${where.single})');
- } else if (skipped > 1) {
+ if (skipped.length == 1) {
+ result.add('(elided one frame from ${skipped.single})');
+ } else if (skipped.length > 1) {
+ final List<String> where = Set<String>.from(skipped).toList()..sort();
if (where.length > 1)
where[where.length - 1] = 'and ${where.last}';
if (where.length > 2) {
- result.add('(elided $skipped frames from ${where.join(", ")})');
+ result.add('(elided ${skipped.length} frames from ${where.join(", ")})');
} else {
- result.add('(elided $skipped frames from ${where.join(" ")})');
+ result.add('(elided ${skipped.length} frames from ${where.join(" ")})');
}
}
return result;
diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart
index ab7af49..8e3d813 100644
--- a/packages/flutter/lib/src/widgets/binding.dart
+++ b/packages/flutter/lib/src/widgets/binding.dart
@@ -254,12 +254,6 @@
void initInstances() {
super.initInstances();
_instance = this;
-
- assert(() {
- _debugAddStackFilters();
- return true;
- }());
-
// Initialization of [_buildOwner] has to be done after
// [super.initInstances] is called, as it requires [ServicesBinding] to
// properly setup the [defaultBinaryMessenger] instance.
@@ -271,84 +265,6 @@
FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
}
- void _debugAddStackFilters() {
- const PartialStackFrame elementInflateWidget = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'inflateWidget');
- const PartialStackFrame elementUpdateChild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'updateChild');
- const PartialStackFrame elementRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'Element', method: 'rebuild');
- const PartialStackFrame componentElementPerformRebuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'performRebuild');
- const PartialStackFrame componentElementFristBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: '_firstBuild');
- const PartialStackFrame componentElementMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'ComponentElement', method: 'mount');
- const PartialStackFrame statefulElementFristBuild = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'StatefulElement', method: '_firstBuild');
- const PartialStackFrame singleChildMount = PartialStackFrame(package: 'package:flutter/src/widgets/framework.dart', className: 'SingleChildRenderObjectElement', method: 'mount');
-
- const String replacementString = '... Normal element mounting';
-
- // ComponentElement variations
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementInflateWidget,
- elementUpdateChild,
- componentElementPerformRebuild,
- elementRebuild,
- componentElementFristBuild,
- componentElementMount,
- ],
- replacement: replacementString,
- ));
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementUpdateChild,
- componentElementPerformRebuild,
- elementRebuild,
- componentElementFristBuild,
- componentElementMount,
- ],
- replacement: replacementString,
- ));
-
- // StatefulElement variations
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementInflateWidget,
- elementUpdateChild,
- componentElementPerformRebuild,
- elementRebuild,
- componentElementFristBuild,
- statefulElementFristBuild,
- componentElementMount,
- ],
- replacement: replacementString,
- ));
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementUpdateChild,
- componentElementPerformRebuild,
- elementRebuild,
- componentElementFristBuild,
- statefulElementFristBuild,
- componentElementMount,
- ],
- replacement: replacementString,
- ));
-
- // SingleChildRenderObjectElement variations
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementInflateWidget,
- elementUpdateChild,
- singleChildMount,
- ],
- replacement: replacementString,
- ));
- FlutterError.addDefaultStackFilter(const RepetitiveStackFrameFilter(
- frames: <PartialStackFrame>[
- elementUpdateChild,
- singleChildMount,
- ],
- replacement: replacementString,
- ));
- }
-
/// The current [WidgetsBinding], if one has been created.
///
/// If you need the binding to be constructed before calling [runApp],
diff --git a/packages/flutter/test/foundation/error_reporting_test.dart b/packages/flutter/test/foundation/error_reporting_test.dart
index 07bb10f..42d5eab 100644
--- a/packages/flutter/test/foundation/error_reporting_test.dart
+++ b/packages/flutter/test/foundation/error_reporting_test.dart
@@ -81,7 +81,7 @@
'#2 getSampleStack \\([^)]+flutter/test/foundation/error_reporting_test\\.dart:[0-9]+:[0-9]+\\)\n'
'#3 main \\([^)]+flutter/test/foundation/error_reporting_test\\.dart:[0-9]+:[0-9]+\\)\n'
'(.+\n)+' // TODO(ianh): when fixing #4021, also filter out frames from the test infrastructure below the first call to our main()
- '\\(elided [0-9]+ frames from class _RawReceivePortImpl and dart:async\\)\n'
+ '\\(elided [0-9]+ frames from package dart:async\\)\n'
'\n'
'line 1 of extra information\n'
'line 2 of extra information\n'
@@ -153,7 +153,7 @@
'#2 getSampleStack \\([^)]+flutter/test/foundation/error_reporting_test\\.dart:[0-9]+:[0-9]+\\)\n'
'#3 main \\([^)]+flutter/test/foundation/error_reporting_test\\.dart:[0-9]+:[0-9]+\\)\n'
'(.+\n)+' // TODO(ianh): when fixing #4021, also filter out frames from the test infrastructure below the first call to our main()
- '\\(elided [0-9]+ frames from class _RawReceivePortImpl and dart:async\\)\n'
+ '\\(elided [0-9]+ frames from package dart:async\\)\n'
'\n'
'line 1 of extra information\n'
'line 2 of extra information\n'
diff --git a/packages/flutter/test/foundation/stack_frame_test.dart b/packages/flutter/test/foundation/stack_frame_test.dart
index 5429878..7fc7724 100644
--- a/packages/flutter/test/foundation/stack_frame_test.dart
+++ b/packages/flutter/test/foundation/stack_frame_test.dart
@@ -71,8 +71,7 @@
}, skip: isBrowser);
}
-const String stackString = '''
-#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39)
+const String stackString = '''#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:38:5)
#2 new Text (package:flutter/src/widgets/text.dart:287:10)
#3 _MyHomePageState.build (package:hello_flutter/main.dart:72:16)
@@ -105,8 +104,7 @@
];
-const String asyncStackString = '''
-#0 getSampleStack.<anonymous closure> (file:///path/to/flutter/packages/flutter/test/foundation/error_reporting_test.dart:40:57)
+const String asyncStackString = '''#0 getSampleStack.<anonymous closure> (file:///path/to/flutter/packages/flutter/test/foundation/error_reporting_test.dart:40:57)
#1 new Future.sync (dart:async/future.dart:224:31)
#2 getSampleStack (file:///path/to/flutter/packages/flutter/test/foundation/error_reporting_test.dart:40:10)
#3 main (file:///path/to/flutter/packages/flutter/test/foundation/error_reporting_test.dart:46:40)
@@ -192,8 +190,7 @@
StackFrame(number: 38, className: '_RawReceivePortImpl', method: '_handleMessage', packageScheme: 'dart', package: 'isolate-patch', packagePath: 'isolate_patch.dart', line: 174, column: 12, source: '#38 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)'),
];
-const String stackFrameNoCols = '''
-#0 blah (package:assertions/main.dart:4)
+const String stackFrameNoCols = '''#0 blah (package:assertions/main.dart:4)
#1 main (package:assertions/main.dart:8)
#2 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:239)
#3 _rootRun (dart:async/zone.dart:1126)
@@ -217,8 +214,7 @@
StackFrame(number: 9, className: '_RawReceivePortImpl', method: '_handleMessage', packageScheme: 'dart', package: 'isolate-patch', packagePath: 'isolate-patch.dart', line: 174, column: -1, source: '#9 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174)'),
];
-const String webStackTrace = r'''
-package:dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 196:49 throw_
+const String webStackTrace = r'''package:dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 196:49 throw_
package:assertions/main.dart 4:3 blah
package:assertions/main.dart 8:5 main$
package:assertions/main_web_entrypoint.dart 9:3 main$
diff --git a/packages/flutter/test/foundation/stack_trace_test.dart b/packages/flutter/test/foundation/stack_trace_test.dart
index 9099668..77b102f 100644
--- a/packages/flutter/test/foundation/stack_trace_test.dart
+++ b/packages/flutter/test/foundation/stack_trace_test.dart
@@ -16,13 +16,13 @@
expect(filtered[0], matches(r'^#0 +main\.<anonymous closure> \(.*stack_trace_test\.dart:[0-9]+:[0-9]+\)$'));
expect(filtered[1], matches(r'^#1 +Declarer\.test\.<anonymous closure>.<anonymous closure>.<anonymous closure> \(package:test_api/.+:[0-9]+:[0-9]+\)$'));
expect(filtered[2], equals('<asynchronous suspension>'));
- expect(filtered.last, matches(r'^\(elided [1-9][0-9]+ frames from class _RawReceivePortImpl, class _Timer, dart:async(, dart:async-patch,)? and package:stack_trace\)$'));
+ expect(filtered.last, matches(r'^\(elided [1-9][0-9]+ frames from package dart:async(, package dart:async-patch,)? and package stack_trace\)$'));
});
test('FlutterError.defaultStackFilter (async test body)', () async {
final List<String> filtered = FlutterError.defaultStackFilter(StackTrace.current.toString().trimRight().split('\n')).toList();
expect(filtered.length, greaterThanOrEqualTo(3));
expect(filtered[0], matches(r'^#0 +main\.<anonymous closure> \(.*stack_trace_test\.dart:[0-9]+:[0-9]+\)$'));
- expect(filtered.last, matches(r'^\(elided [1-9][0-9]+ frames from class _RawReceivePortImpl, class _Timer, dart:async(, dart:async-patch,)? and package:stack_trace\)$'));
+ expect(filtered.last, matches(r'^\(elided [1-9][0-9]+ frames from package dart:async(, package dart:async-patch,)? and package stack_trace\)$'));
});
}
diff --git a/packages/flutter/test/widgets/binding_test.dart b/packages/flutter/test/widgets/binding_test.dart
index c769636..362a380 100644
--- a/packages/flutter/test/widgets/binding_test.dart
+++ b/packages/flutter/test/widgets/binding_test.dart
@@ -2,10 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:async';
import 'dart:typed_data';
-import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@@ -139,12 +137,6 @@
tester.binding.scheduleWarmUpFrame(); // this actually tests flutter_test's implementation
expect(tester.binding.hasScheduledFrame, isFalse);
expect(frameCount, 1);
-
- // Get the tester back to a resumed state for subsequent tests.
- message = const StringCodec().encodeMessage('AppLifecycleState.resumed');
- await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
- expect(tester.binding.hasScheduledFrame, isTrue);
- await tester.pump();
});
testWidgets('scheduleFrameCallback error control test', (WidgetTester tester) async {
@@ -178,34 +170,4 @@
' argument.\n'
);
});
-
- testWidgets('defaultStackFilter elides framework Element mounting stacks', (WidgetTester tester) async {
- final FlutterExceptionHandler oldHandler = FlutterError.onError;
- String filteredStack;
- FlutterError.onError = (FlutterErrorDetails details) {
- expect(details.exception, isAssertionError);
- expect(filteredStack, isNull);
- filteredStack = details.toString();
- };
- await tester.pumpWidget(Directionality(
- textDirection: TextDirection.ltr,
- child: FocusableActionDetector(
- child: Builder(
- builder: (BuildContext context) {
- return Opacity(
- opacity: .5,
- child: Builder(
- builder: (BuildContext context) => Text(null),
- ),
- );
- },
- ),
- ),
- ));
- // We don't elide the root or the last element.
- FlutterError.onError = oldHandler;
- expect(tester.allElements.length, 10);
- print(filteredStack);
- expect(filteredStack, contains('... Normal element mounting (42 frames)'));
- });
}