From 27a71fc7b2adefba6ab4c6c0dc3dd2ee6513d0ce Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Wed, 6 Jul 2016 16:13:35 -0400 Subject: [PATCH] [Mono.Android] Java.Lang.Throwable overrides Exception.ToString() Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=42366 A funny thing happened between Xamarin.Android 6.0 and 6.1: we bumped from Mono 4.2 to Mono 4.4, and Mono 4.4 now uses Microsoft's referencesource for `System.Exception`. In Mono 4.2, `System.Exception.ToString()` accesses the `System.Exception.StackTrace` property. Since `Java.Lang.Throwable` overrode the `Exception.StackTrace` property to append the Java-side stack trace, this was sufficient to cause `.ToString()` usage to likewise contain the java-side stack: var e = new Java.Lang.Throwable ("Boo!"); var s = e.ToString (); // s contains "... --- End of managed exception stack trace --- ..." This is *not* the case in Mono 4.4: `Exception.ToString()` *doesn't* use the `Exception.StackTrace` property, so the `Throwable.StackTrace` override is ignored, making for less-than-useful `.ToString()` value: // s == "Java.Lang.Throwable: Exception of type 'Java.Lang.Throwable' was thrown" // s does NOT contain the Java-side callstack! Update `Java.Lang.Throwable` to override `Exception.ToString()` so that the Java-side stack trace is always appended, if present. This allows `Exception.ToString()` to be far more useful: Java.Lang.Throwable: Exception of type 'Java.Lang.Throwable' was thrown. --- End of managed Java.Lang.Throwable stack trace --- java.lang.Throwable: Boo! at md5bb7ab8908f0a1e03a3f881aa9f8f5eef.MainActivity.n_onCreate(Native Method) at md5bb7ab8908f0a1e03a3f881aa9f8f5eef.MainActivity.onCreate(MainActivity.java:29) ... --- src/Mono.Android/Java.Lang/Throwable.cs | 44 +++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Mono.Android/Java.Lang/Throwable.cs b/src/Mono.Android/Java.Lang/Throwable.cs index f3005edbaad..d562565096d 100644 --- a/src/Mono.Android/Java.Lang/Throwable.cs +++ b/src/Mono.Android/Java.Lang/Throwable.cs @@ -13,7 +13,6 @@ public partial class Throwable : global::System.Exception, IJavaObject, IDisposa #endif // JAVA_INTEROP { - static IntPtr java_lang_Object_toString; protected bool is_generated; IntPtr handle; @@ -130,20 +129,45 @@ IntPtr IJavaObjectEx.ToLocalJniHandle () public override string StackTrace { get { - if (string.IsNullOrEmpty (nativeStack) && handle != IntPtr.Zero) { - using (var nativeStackWriter = new Java.IO.StringWriter ()) - using (var nativeStackPw = new Java.IO.PrintWriter (nativeStackWriter)) { - PrintStackTrace (nativeStackPw); - nativeStack = nativeStackWriter.ToString (); - } - nativeStack = base.StackTrace + Environment.NewLine + - " --- End of managed exception stack trace ---" + Environment.NewLine + - nativeStack; + return base.StackTrace + ManagedStackTraceAddendum; + } + } + + string ManagedStackTraceAddendum { + get { + var javaStack = JavaStackTrace; + if (string.IsNullOrEmpty (javaStack)) + return ""; + return Environment.NewLine + + " --- End of managed " + + GetType ().FullName + + " stack trace ---" + Environment.NewLine + + javaStack; + } + } + + string JavaStackTrace { + get { + if (!string.IsNullOrEmpty (nativeStack)) + return nativeStack; + + if (handle == IntPtr.Zero) + return null; + + using (var nativeStackWriter = new Java.IO.StringWriter ()) + using (var nativeStackPw = new Java.IO.PrintWriter (nativeStackWriter)) { + PrintStackTrace (nativeStackPw); + nativeStack = nativeStackWriter.ToString (); } return nativeStack; } } + public override string ToString () + { + return base.ToString () + ManagedStackTraceAddendum; + } + #if JAVA_INTEROP public int JniIdentityHashCode { get {return (int) key_handle;}