|
|
|
|
|
|
namespace Unity.UIWidgets.foundation { |
|
|
|
public delegate void UIWidgetsExceptionHandler(UIWidgetsErrorDetails details); |
|
|
|
|
|
|
|
public delegate void InformationCollector(StringBuilder information); |
|
|
|
public delegate IEnumerable<DiagnosticsNode> DiagnosticPropertiesTransformer(IEnumerable<DiagnosticsNode> properties); |
|
|
|
|
|
|
|
public delegate IEnumerable<DiagnosticsNode> InformationCollector(); |
|
|
|
|
|
|
|
internal abstract class _ErrorDiagnostic : DiagnosticsProperty<List<object>> { |
|
|
|
internal _ErrorDiagnostic( |
|
|
|
String message, |
|
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.flat, |
|
|
|
DiagnosticLevel level = DiagnosticLevel.info |
|
|
|
) : base( |
|
|
|
name: null, |
|
|
|
value: new List<object>() {message}, |
|
|
|
showName: false, |
|
|
|
showSeparator: false, |
|
|
|
defaultValue: null, |
|
|
|
style: style, |
|
|
|
level: level) { |
|
|
|
D.assert(message != null); |
|
|
|
} |
|
|
|
|
|
|
|
internal _ErrorDiagnostic( |
|
|
|
List<object> messageParts, |
|
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.flat, |
|
|
|
DiagnosticLevel level = DiagnosticLevel.info |
|
|
|
) : base( |
|
|
|
name: null, |
|
|
|
value: messageParts, |
|
|
|
showName: false, |
|
|
|
showSeparator: false, |
|
|
|
defaultValue: null, |
|
|
|
style: style, |
|
|
|
level: level) { |
|
|
|
D.assert(messageParts != null); |
|
|
|
} |
|
|
|
|
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) { |
|
|
|
return string.Join("", value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class ErrorDescription : _ErrorDiagnostic { |
|
|
|
public ErrorDescription(string message) : base(message, level: DiagnosticLevel.info) { |
|
|
|
} |
|
|
|
|
|
|
|
public ErrorDescription(List<object> messageParts) : base(messageParts, level: DiagnosticLevel.info) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class ErrorSummary : _ErrorDiagnostic { |
|
|
|
public ErrorSummary(string message) : base(message, level: DiagnosticLevel.summary) { |
|
|
|
} |
|
|
|
|
|
|
|
public ErrorSummary(List<object> messageParts) : base(messageParts, level: DiagnosticLevel.summary) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class ErrorHint : _ErrorDiagnostic { |
|
|
|
public ErrorHint(string message) : base(message, level: DiagnosticLevel.hint) { |
|
|
|
} |
|
|
|
|
|
|
|
public ErrorHint(List<object> messageParts) : base(messageParts, level: DiagnosticLevel.hint) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class ErrorSpacer : DiagnosticsProperty<object> { |
|
|
|
public ErrorSpacer() : base( |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class UIWidgetsErrorDetails { |
|
|
|
public class UIWidgetsErrorDetails : Diagnosticable { |
|
|
|
string context = null, |
|
|
|
DiagnosticsNode context = null, |
|
|
|
IterableFilter<string> stackFilter = null, |
|
|
|
InformationCollector informationCollector = null, |
|
|
|
bool silent = false |
|
|
|
|
|
|
this.silent = silent; |
|
|
|
} |
|
|
|
|
|
|
|
public static readonly List<DiagnosticPropertiesTransformer> propertiesTransformers = |
|
|
|
new List<DiagnosticPropertiesTransformer>(); |
|
|
|
|
|
|
|
|
|
|
|
public readonly string context; |
|
|
|
|
|
|
|
public readonly DiagnosticsNode context; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public readonly bool silent; |
|
|
|
|
|
|
|
public string exceptionAsString() { |
|
|
|
|
|
|
return longMessage; |
|
|
|
} |
|
|
|
|
|
|
|
public override string ToString() { |
|
|
|
var buffer = new StringBuilder(); |
|
|
|
if (library.isNotEmpty() || context.isNotEmpty()) { |
|
|
|
if (library.isNotEmpty()) { |
|
|
|
buffer.AppendFormat("Error caught by {0}", library); |
|
|
|
if (context.isNotEmpty()) { |
|
|
|
buffer.Append(", "); |
|
|
|
} |
|
|
|
Diagnosticable _exceptionToDiagnosticable() { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
public DiagnosticsNode summary { |
|
|
|
get { |
|
|
|
string formatException() { |
|
|
|
return exceptionAsString().Split('\n')[0].TrimStart(); |
|
|
|
else { |
|
|
|
buffer.Append("Exception "); |
|
|
|
|
|
|
|
if (foundation_.kReleaseMode) { |
|
|
|
return DiagnosticsNode.message(formatException()); |
|
|
|
if (context.isNotEmpty()) { |
|
|
|
buffer.AppendFormat("thrown {0}", context); |
|
|
|
Diagnosticable diagnosticable = _exceptionToDiagnosticable(); |
|
|
|
DiagnosticsNode summary = null; |
|
|
|
if (diagnosticable != null) { |
|
|
|
DiagnosticPropertiesBuilder builder = new DiagnosticPropertiesBuilder(); |
|
|
|
debugFillProperties(builder); |
|
|
|
summary = builder.properties.First((DiagnosticsNode node) => node.level == DiagnosticLevel.summary); |
|
|
|
buffer.Append(". "); |
|
|
|
return summary ?? new ErrorSummary(formatException()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|
|
|
base.debugFillProperties(properties); |
|
|
|
DiagnosticsNode verb = context != null |
|
|
|
? new ErrorDescription($"thrown {new ErrorDescription($" {context}")}") |
|
|
|
: new ErrorDescription(""); |
|
|
|
|
|
|
|
Diagnosticable diagnosticable = _exceptionToDiagnosticable(); |
|
|
|
if (exception is NullReferenceException) { |
|
|
|
properties.add(new ErrorDescription($"The null value was {verb}.")); |
|
|
|
buffer.Append("An error was caught. "); |
|
|
|
DiagnosticsNode errorName; |
|
|
|
errorName = new ErrorDescription($"{exception.GetType()}"); |
|
|
|
properties.add(new ErrorDescription($"The following {errorName} was {verb}:")); |
|
|
|
if (diagnosticable != null) { |
|
|
|
diagnosticable.debugFillProperties(properties); |
|
|
|
} |
|
|
|
else { |
|
|
|
string prefix = $"{exception.GetType()}"; |
|
|
|
string message = exceptionAsString(); |
|
|
|
if (message.StartsWith(prefix)) { |
|
|
|
message = message.Substring(prefix.Length); |
|
|
|
} |
|
|
|
|
|
|
|
properties.add(new ErrorSummary(message)); |
|
|
|
} |
|
|
|
buffer.AppendLine(exceptionAsString()); |
|
|
|
informationCollector(buffer); |
|
|
|
properties.add(new ErrorSpacer()); |
|
|
|
foreach (var diagnosticsNode in informationCollector()) { |
|
|
|
properties.add(diagnosticsNode); |
|
|
|
} |
|
|
|
} |
|
|
|
if (exception.StackTrace != null) { |
|
|
|
IEnumerable<string> stackLines = exception.StackTrace.TrimEnd().Split('\n'); |
|
|
|
if (stackFilter != null) { |
|
|
|
stackLines = stackFilter(stackLines); |
|
|
|
public override string toStringShort() { |
|
|
|
return library != null ? $"Exception caught by {library}" : "Exception caught"; |
|
|
|
} |
|
|
|
|
|
|
|
public override string toString(DiagnosticLevel minLevel = DiagnosticLevel.info) { |
|
|
|
return toDiagnosticsNode(style: DiagnosticsTreeStyle.error).toStringDeep(minLevel: minLevel); |
|
|
|
} |
|
|
|
|
|
|
|
public override DiagnosticsNode toDiagnosticsNode(string name = null, |
|
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.sparse) { |
|
|
|
return new _UIWIdgetsErrorDetailsNode( |
|
|
|
name: name, |
|
|
|
value: this, |
|
|
|
style: style |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal class _UIWIdgetsErrorDetailsNode : DiagnosticableNode<UIWidgetsErrorDetails> { |
|
|
|
public _UIWIdgetsErrorDetailsNode( |
|
|
|
string name, |
|
|
|
UIWidgetsErrorDetails value, |
|
|
|
DiagnosticsTreeStyle style |
|
|
|
) : base( |
|
|
|
name: name, |
|
|
|
value: value, |
|
|
|
style: style |
|
|
|
) { |
|
|
|
D.assert(value != null); |
|
|
|
} |
|
|
|
|
|
|
|
new DiagnosticPropertiesBuilder builder { |
|
|
|
get { |
|
|
|
DiagnosticPropertiesBuilder builder = base.builder; |
|
|
|
if (builder == null) { |
|
|
|
return null; |
|
|
|
else { |
|
|
|
stackLines = UIWidgetsError.defaultStackFilter(stackLines); |
|
|
|
|
|
|
|
IEnumerable<DiagnosticsNode> properties = builder.properties; |
|
|
|
foreach (DiagnosticPropertiesTransformer transformer in UIWidgetsErrorDetails.propertiesTransformers) { |
|
|
|
properties = transformer(properties); |
|
|
|
buffer.Append(string.Join("\n", stackLines.ToArray())); |
|
|
|
return new DiagnosticPropertiesBuilder(properties.ToList()); |
|
|
|
|
|
|
|
return buffer.ToString().TrimEnd(); |
|
|
|
|
|
|
|
public UIWidgetsError(string message) : base(message) { |
|
|
|
public UIWidgetsError(string message) { |
|
|
|
string[] lines = message.Split('\n'); |
|
|
|
diagnostics = new List<DiagnosticsNode>() { |
|
|
|
new ErrorSummary(lines[0]) |
|
|
|
}; |
|
|
|
|
|
|
|
for (var i = 1; i < lines.Length; i++) { |
|
|
|
diagnostics.Add(new ErrorDescription(lines[i])); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public UIWidgetsError(List<DiagnosticsNode> diagnostics) { |
|
|
|
this.diagnostics = diagnostics; |
|
|
|
D.assert(diagnostics != null && diagnostics.isNotEmpty(), () => new UIWidgetsError(new List<DiagnosticsNode>() {new ErrorSummary("Empty FlutterError")}).ToString()); |
|
|
|
D.assert(diagnostics.first().level == DiagnosticLevel.summary, |
|
|
|
() => new UIWidgetsError(new List<DiagnosticsNode>() { |
|
|
|
new ErrorSummary("UIWidgetsError is missing a summary."), |
|
|
|
new ErrorDescription("All UIWidgetsError objects should start with a short (one line) " + |
|
|
|
"summary description of the problem that was detected."), |
|
|
|
new DiagnosticsProperty<UIWidgetsError>("Malformed", this, expandableValue: true, showSeparator: false, style : DiagnosticsTreeStyle.whitespace), |
|
|
|
new ErrorDescription( |
|
|
|
"\nThis error should still help you solve your problem, " + |
|
|
|
"however please also report this malformed error in the " + |
|
|
|
"framework by filing a bug on GitHub:\n" + |
|
|
|
" https://https://github.com/Unity-Technologies/com.unity.uiwidgets" |
|
|
|
) |
|
|
|
}).ToString()); |
|
|
|
|
|
|
|
D.assert(() => { |
|
|
|
IEnumerable<DiagnosticsNode> summaries = |
|
|
|
diagnostics.Where((DiagnosticsNode node) => node.level == DiagnosticLevel.summary); |
|
|
|
if (summaries.Count() > 1) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
}, () => { |
|
|
|
IEnumerable<DiagnosticsNode> summaries = |
|
|
|
diagnostics.Where((DiagnosticsNode node) => node.level == DiagnosticLevel.summary); |
|
|
|
List<DiagnosticsNode> message = new List<DiagnosticsNode>() { |
|
|
|
new ErrorSummary("UIWidgetsError contained multiple error summaries."), |
|
|
|
new ErrorDescription( |
|
|
|
"All UIWidgetsError objects should have only a single short " + |
|
|
|
"(one line) summary description of the problem that was " + |
|
|
|
"detected." |
|
|
|
), |
|
|
|
new DiagnosticsProperty<UIWidgetsError>("Malformed", this, expandableValue: true, showSeparator: false, style : DiagnosticsTreeStyle.whitespace) |
|
|
|
}; |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
foreach (DiagnosticsNode summary in summaries) { |
|
|
|
message.Add(new DiagnosticsProperty<DiagnosticsNode>($"Summary {i}", summary, expandableValue : true)); |
|
|
|
i += 1; |
|
|
|
} |
|
|
|
message.Add(new ErrorDescription( |
|
|
|
"\nThis error should still help you solve your problem, " + |
|
|
|
"however please also report this malformed error in the " + |
|
|
|
"framework by filing a bug on GitHub:\n" + |
|
|
|
" https://https://github.com/Unity-Technologies/com.unity.uiwidgets" |
|
|
|
)); |
|
|
|
|
|
|
|
return new UIWidgetsError(message).ToString(); |
|
|
|
}); |
|
|
|
|
|
|
|
public readonly List<DiagnosticsNode> diagnostics; |
|
|
|
|
|
|
|
public override string Message => ToString(); |
|
|
|
static int _errorCount = 0; |
|
|
|
|
|
|
|
public static void resetErrorCount() { |
|
|
|
_errorCount = 0; |
|
|
|
} |
|
|
|
|
|
|
|
const int wrapWidth = 100; |
|
|
|
|
|
|
|
public static void dumpErrorToConsole(UIWidgetsErrorDetails details) { |
|
|
|
dumpErrorToConsole(details, forceReport: false); |
|
|
|
} |
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
D.logError(details.ToString(), details.exception); |
|
|
|
if (_errorCount == 0 || forceReport) { |
|
|
|
D.logError(new TextTreeRenderer( |
|
|
|
wrapWidth: wrapWidth, |
|
|
|
wrapWidthProperties: wrapWidth, |
|
|
|
maxDescendentsTruncatableNode: 5).render( |
|
|
|
details.toDiagnosticsNode(style: DiagnosticsTreeStyle.error)).TrimEnd(), details.exception); |
|
|
|
} |
|
|
|
|
|
|
|
D.logError($"Another exception was thrown: ${details.summary}"); |
|
|
|
|
|
|
|
_errorCount += 1; |
|
|
|
} |
|
|
|
|
|
|
|
public static IEnumerable<string> defaultStackFilter(IEnumerable<string> frames) { |
|
|
|
|
|
|
public static void reportError(UIWidgetsErrorDetails details) { |
|
|
|
D.assert(details != null); |
|
|
|
D.assert(details.exception != null); |
|
|
|
if (onError != null) { |
|
|
|
onError(details); |
|
|
|
public void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
|
|
|
foreach (var diagnosticsNode in diagnostics) |
|
|
|
{ |
|
|
|
properties.add(diagnosticsNode); |
|
|
|
} |
|
|
|
internal abstract class _ErrorDiagnostic : 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
|
|
|
|
/// interactive display of errors.
|
|
|
|
internal _ErrorDiagnostic( |
|
|
|
String message, |
|
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.sparse, // DiagnosticsTreeStyle.flat
|
|
|
|
DiagnosticLevel level = DiagnosticLevel.info |
|
|
|
) : base( |
|
|
|
name: null, |
|
|
|
value: new List<object>() {message}, |
|
|
|
showName: false, |
|
|
|
showSeparator: false, |
|
|
|
defaultValue: null, |
|
|
|
style: style, |
|
|
|
level: level) { |
|
|
|
D.assert(message != null); |
|
|
|
} |
|
|
|
public string toStringShort() => "UIWidgetsError"; |
|
|
|
/// In debug builds, a kernel transformer rewrites calls to the default
|
|
|
|
/// constructors for [ErrorSummary], [ErrorDetails], and [ErrorHint] to use
|
|
|
|
/// this constructor.
|
|
|
|
//
|
|
|
|
// ```dart
|
|
|
|
// _ErrorDiagnostic('Element $element must be $color')
|
|
|
|
// ```
|
|
|
|
// Desugars to:
|
|
|
|
// ```dart
|
|
|
|
// _ErrorDiagnostic.fromParts(<Object>['Element ', element, ' must be ', color])
|
|
|
|
// ```
|
|
|
|
//
|
|
|
|
// Slightly more complex case:
|
|
|
|
// ```dart
|
|
|
|
// _ErrorDiagnostic('Element ${element.runtimeType} must be $color')
|
|
|
|
// ```
|
|
|
|
// Desugars to:
|
|
|
|
//```dart
|
|
|
|
// _ErrorDiagnostic.fromParts(<Object>[
|
|
|
|
// 'Element ',
|
|
|
|
// DiagnosticsProperty(null, element, description: element.runtimeType?.toString()),
|
|
|
|
// ' must be ',
|
|
|
|
// color,
|
|
|
|
// ])
|
|
|
|
// ```
|
|
|
|
internal _ErrorDiagnostic( |
|
|
|
List<object> messageParts, |
|
|
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.sparse, |
|
|
|
DiagnosticLevel level = DiagnosticLevel.info |
|
|
|
) : base( |
|
|
|
name: null, |
|
|
|
value: messageParts, |
|
|
|
showName: false, |
|
|
|
showSeparator: false, |
|
|
|
defaultValue: null, |
|
|
|
style: style, |
|
|
|
level: level) { |
|
|
|
D.assert(messageParts != null); |
|
|
|
public override string ToString() { |
|
|
|
TextTreeRenderer renderer = new TextTreeRenderer(wrapWidth: 400000000); |
|
|
|
return string.Join("\n", diagnostics.Select((DiagnosticsNode node) => renderer.render(node).TrimEnd())); |
|
|
|
protected override string valueToString(TextTreeConfiguration parentConfiguration = null) { |
|
|
|
return String.Join("", value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class ErrorDescription : _ErrorDiagnostic { |
|
|
|
/// A lint enforces that this constructor can only be called with a string
|
|
|
|
/// literal to match the limitations of the Dart Kernel transformer that
|
|
|
|
/// optionally extracts out objects referenced using string interpolation in
|
|
|
|
/// the message passed in.
|
|
|
|
///
|
|
|
|
/// The message will display with the same text regardless of whether the
|
|
|
|
/// kernel transformer is used. The kernel transformer is required so that
|
|
|
|
/// debugging tools can provide interactive displays of objects described by
|
|
|
|
/// the error.
|
|
|
|
public ErrorDescription(string message) : base(message, level: DiagnosticLevel.info) { |
|
|
|
} |
|
|
|
|
|
|
|
/// Calls to the default constructor may be rewritten to use this constructor
|
|
|
|
/// in debug mode using a kernel transformer.
|
|
|
|
// ignore: unused_element
|
|
|
|
public ErrorDescription(List<object> messageParts) : base(messageParts, level: DiagnosticLevel.info) { |
|
|
|
public static void reportError(UIWidgetsErrorDetails details) { |
|
|
|
D.assert(details != null); |
|
|
|
D.assert(details.exception != null); |
|
|
|
if (onError != null) { |
|
|
|
onError(details); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |