Commit daf5c312 authored by Hixie's avatar Hixie

Improve exception reporting in Widgets framework

This specifically improves the reporting of exceptions in syncChild(),
and makes the way we've been adding information to toStringName() less
ad-hoc and easier to extend.
parent dfd821e5
......@@ -140,7 +140,6 @@ abstract class GlobalKey extends Key {
if (_syncedKeys.isEmpty && _removedKeys.isEmpty)
return;
try {
for (GlobalKey key in _syncedKeys) {
Widget widget = _registry[key];
if (widget != null && _syncListeners.containsKey(key)) {
......@@ -149,7 +148,6 @@ abstract class GlobalKey extends Key {
listener(key, widget);
}
}
for (GlobalKey key in _removedKeys) {
if (!_registry.containsKey(key) && _removeListeners.containsKey(key)) {
Set<GlobalKeyRemoveListener> localListeners = new Set<GlobalKeyRemoveListener>.from(_removeListeners[key]);
......@@ -388,6 +386,13 @@ abstract class Widget {
// Returns the child which should be retained as the child of this node.
Widget syncChild(Widget newNode, Widget oldNode, dynamic slot) {
String debugDetails;
assert(() {
// we save this information early because by the time the exception fires we might have changed everything around
debugDetails = " old child: ${oldNode?.toStringName()}\n new child: ${newNode?.toStringName()}";
return true;
});
try {
if (newNode == oldNode) {
// TODO(ianh): Simplify next few asserts once the analyzer is cleverer
......@@ -483,6 +488,11 @@ abstract class Widget {
newNode._sync(oldNode, slot);
assert(newNode.renderObject is RenderObject);
return newNode;
} catch (e, stack) {
_debugReportException('syncing children of ${this.toStringName()}\n$debugDetails', e, stack);
return null;
}
}
String _adjustPrefixWithParentCheck(Widget child, String prefix) {
......@@ -507,19 +517,25 @@ abstract class Widget {
nextPrefix = prefix + ' ';
childrenString += lastChild.toString(nextPrefix, _adjustPrefixWithParentCheck(lastChild, nextStartPrefix));
}
String suffix = '';
return '$startPrefix${toStringName()}\n$childrenString';
}
String toStringName() {
List<String> details = <String>[];
debugAddDetails(details);
String detailString = details.join('; ');
return '$runtimeType($detailString})';
}
void debugAddDetails(List<String> details) {
if (key != null)
details.add('$key');
details.add('hashCode=$hashCode');
details.add(mounted ? 'mounted' : 'not mounted');
String generationString = '';
if (_generation != _currentGeneration) {
int delta = _generation - _currentGeneration;
String sign = delta < 0 ? '' : '+';
suffix = ' gen$sign$delta';
}
return '$startPrefix${toStringName()}$suffix\n$childrenString';
details.add('gen$sign$delta');
}
String toStringName() {
String keyString = key == null ? '' : '$key; ';
String hashCodeString = 'hashCode=$hashCode';
String mountedString = mounted ? '; mounted' : '; not mounted';
return '$runtimeType($keyString$hashCodeString$mountedString)';
}
// This function can be safely called when the layout is valid.
......@@ -902,6 +918,11 @@ abstract class Component extends Widget {
Widget build();
void debugAddDetails(List<String> details) {
super.debugAddDetails(details);
if (_dirty)
details.add('dirty');
}
}
abstract class StatefulComponent extends Component {
......@@ -964,10 +985,9 @@ abstract class StatefulComponent extends Component {
_scheduleBuild();
}
String toStringName() {
if (_isStateInitialized)
return 'Stateful ${super.toStringName()}';
return 'Stateless ${super.toStringName()}';
void debugAddDetails(List<String> details) {
super.debugAddDetails(details);
details.add(_isStateInitialized ? 'stateful' : 'stateless');
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment