From d99641dd85bc5f820028d666f2491aabac123157 Mon Sep 17 00:00:00 2001 From: Hixie Date: Mon, 24 Aug 2015 15:11:41 -0700 Subject: [PATCH] Better exception handling for rendering library. - Catch exceptions closer to the source. - Factor out exception printing code. - Have widget library hand the rendering library some context when syncing RenderObjectWrappers to aid with debugging. - Fix a bug in flex.dart whereby _overflow was compared when null. --- packages/flutter/lib/rendering/flex.dart | 2 +- packages/flutter/lib/rendering/object.dart | 47 +++++++++++++++------ packages/flutter/lib/widgets/framework.dart | 8 ++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/flutter/lib/rendering/flex.dart b/packages/flutter/lib/rendering/flex.dart index a546f96fec..f0980a811f 100644 --- a/packages/flutter/lib/rendering/flex.dart +++ b/packages/flutter/lib/rendering/flex.dart @@ -512,7 +512,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin 0.0) + if (_overflow is double && _overflow > 0.0) header += ' OVERFLOWING'; return header; } diff --git a/packages/flutter/lib/rendering/object.dart b/packages/flutter/lib/rendering/object.dart index 0334696cf1..4c6630eda8 100644 --- a/packages/flutter/lib/rendering/object.dart +++ b/packages/flutter/lib/rendering/object.dart @@ -318,6 +318,24 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { // Override in subclasses with children and call the visitor for each child. void visitChildren(RenderObjectVisitor visitor) { } + dynamic debugExceptionContext = ''; + static dynamic _debugLastException; + bool _debugReportException(dynamic exception, String method) { + if (!inDebugBuild) { + print('Uncaught exception in ${method}():\n$exception'); + return false; + } + if (!identical(exception, _debugLastException)) { + print('-- EXCEPTION --'); + print('An exception was raised during ${method}().'); + 'The following RenderObject was being processed when the exception was fired:\n${this}'.split('\n').forEach(print); + if (debugExceptionContext != '') + 'The RenderObject had the following exception context:\n${debugExceptionContext}'.split('\n').forEach(print); + _debugLastException = exception; + } + return true; + } + static bool _debugDoingLayout = false; static bool get debugDoingLayout => _debugDoingLayout; bool _debugDoingThisResize = false; @@ -443,10 +461,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { return true; }); } catch (e) { - print('Exception raised during layout:\n${e}\nContext:\n${this}'); - if (inDebugBuild) + if (_debugReportException(e, 'layoutWithoutResize')) rethrow; - return; } _needsLayout = false; markNeedsPaint(); @@ -482,14 +498,19 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { _debugActiveLayout = this; return true; }); - performLayout(); - assert(() { - _debugActiveLayout = debugPreviousActiveLayout; - _debugDoingThisLayout = false; - _debugMutationsLocked = false; - return true; - }); - assert(debugDoesMeetConstraints()); + try { + performLayout(); + assert(() { + _debugActiveLayout = debugPreviousActiveLayout; + _debugDoingThisLayout = false; + _debugMutationsLocked = false; + return true; + }); + assert(debugDoesMeetConstraints()); + } catch (e) { + if (_debugReportException(e, 'layout')) + rethrow; + } _needsLayout = false; markNeedsPaint(); assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer is cleverer @@ -666,10 +687,8 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { _paintWithContext(context, Offset.zero); context.endRecording(); } catch (e) { - print('Exception raised during _paintLayer:\n${e}\nContext:\n${this}'); - if (inDebugBuild) + if (_debugReportException(e, '_repaint')) rethrow; - return; } } void _paintWithContext(PaintingContext context, Offset offset) { diff --git a/packages/flutter/lib/widgets/framework.dart b/packages/flutter/lib/widgets/framework.dart index ec93c0c9b6..7e872e7cd9 100644 --- a/packages/flutter/lib/widgets/framework.dart +++ b/packages/flutter/lib/widgets/framework.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:sky' as sky; +import 'package:sky/base/debug.dart'; import 'package:sky/base/hit_test.dart'; import 'package:sky/base/scheduler.dart' as scheduler; import 'package:sky/mojo/activity.dart'; @@ -955,6 +956,13 @@ abstract class RenderObjectWrapper extends Widget { _ancestor = old._ancestor; assert(_renderObject != null); } + if (inDebugBuild) { + try { + throw null; + } catch (_, stack) { + _renderObject.debugExceptionContext = stack; + } + } assert(_renderObject == renderObject); // in case a subclass reintroduces it assert(renderObject != null); assert(mounted);