From 7d2248295922a10ff00617a2b35c46fbc8c602d7 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 31 Mar 2017 20:12:12 +0200 Subject: [PATCH] [objc] Add error handling. (#33) --- docs/error.md | 40 ++++++ objcgen/ErrorHelper.cs | 176 +++++++++++++++++++++++++ objcgen/driver.cs | 14 +- objcgen/error.cs | 49 +++++++ objcgen/objcgen.csproj | 5 + objcgen/objcgenerator.cs | 6 +- tests/objcgentest/ObjCGeneratorTest.cs | 9 ++ 7 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 docs/error.md create mode 100644 objcgen/ErrorHelper.cs create mode 100644 objcgen/error.cs diff --git a/docs/error.md b/docs/error.md new file mode 100644 index 0000000..53ee08e --- /dev/null +++ b/docs/error.md @@ -0,0 +1,40 @@ +id:{932C3F0C-D968-42D1-BB14-D97C73361983} +title:Embeddinator-4000 errors + +[//]: # (The original file resides under https://github.com/mono/Embeddinator-4000/tree/master/docs/error.md) +[//]: # (This allows all contributors (including external) to submit, using a PR, updates to the documentation that match the tools changes) +[//]: # (Modifications outside of mono/Embeddinator-4000 will be lost on future updates) + +# EM0xxx: binding error messages + +E.g. parameters, environment + + +

EM0000: Unexpected error - Please fill a bug report at https://github.com/mono/Embeddinator-4000/issues

+ +An unexpected error condition occurred. Please [file an issue](https://github.com/mono/Embeddinator-4000/issues) with as much information as possible, including: + +* Full build logs, with maximum verbosity +* A minimal test case that reproduce the error +* All version informations + +The easiest way to get exact version information is to use the **Xamarin Studio** menu, **About Xamarin Studio** item, **Show Details** button and copy/paste the version informations (you can use the **Copy Information** button). + +

EM0099: Internal error *. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).

+ +This error message is reported when an internal consistency check in the Embeddinator-4000 fails. + +This indicates a bug in the Embeddinator-4000; please file a bug report at [https://github.com/mono/Embeddinator-4000/issues](https://github.com/mono/Embeddinator-4000/issues) with a test case. + +# EM1xxx: code generation + + + + + + + + + + + diff --git a/objcgen/ErrorHelper.cs b/objcgen/ErrorHelper.cs new file mode 100644 index 0000000..7ade06c --- /dev/null +++ b/objcgen/ErrorHelper.cs @@ -0,0 +1,176 @@ +// Copyright 2014, Xamarin Inc. All rights reserved, + +using System; +using System.Collections.Generic; + +using ProductException = Embeddinator.EmbeddinatorException; + +namespace Embeddinator +{ + static class ErrorHelper + { + public enum WarningLevel + { + Error = -1, + Warning = 0, + Disable = 1, + } + const string Prefix = ProductException.PREFIX; + static Dictionary warning_levels; + public static int Verbosity { get; set; } + + public static WarningLevel GetWarningLevel (int code) + { + WarningLevel level; + + if (warning_levels == null) + return WarningLevel.Warning; + + // code -1: all codes + if (warning_levels.TryGetValue (-1, out level)) + return level; + + if (warning_levels.TryGetValue (code, out level)) + return level; + + return WarningLevel.Warning; ; + } + + public static void SetWarningLevel (WarningLevel level, int? code = null /* if null, apply to all warnings */) + { + if (warning_levels == null) + warning_levels = new Dictionary (); + if (code.HasValue) { + warning_levels [code.Value] = level; + } else { + warning_levels [-1] = level; // code -1: all codes. + } + } + + public static ProductException CreateError (int code, string message, params object [] args) + { + return new ProductException (code, true, message, args); + } + + public static ProductException CreateError (int code, Exception innerException, string message, params object [] args) + { + return new ProductException (code, true, innerException, message, args); + } + + public static ProductException CreateWarning (int code, string message, params object [] args) + { + return new ProductException (code, false, message, args); + } + + public static void Error (int code, Exception innerException, string message, params object [] args) + { + throw new ProductException (code, true, innerException, message, args); + } + + public static void Error (int code, string message, params object [] args) + { + throw new ProductException (code, true, message, args); + } + + public static void Warning (int code, string message, params object [] args) + { + Show (new ProductException (code, false, message, args)); + } + + public static void Warning (int code, Exception innerException, string message, params object [] args) + { + Show (new ProductException (code, false, innerException, message, args)); + } + + public static void Show (IEnumerable list) + { + List exceptions = new List (); + bool error = false; + + foreach (var e in list) + CollectExceptions (e, exceptions); + + foreach (var ex in exceptions) + error |= ShowInternal (ex); + + if (error) + Exit (1); + } + + static public void Show (Exception e) + { + List exceptions = new List (); + bool error = false; + + CollectExceptions (e, exceptions); + + foreach (var ex in exceptions) + error |= ShowInternal (ex); + + if (error) + Exit (1); + } + + static void Exit (int exitCode) + { + Environment.Exit (exitCode); + } + + static void CollectExceptions (Exception ex, List exceptions) + { + AggregateException ae = ex as AggregateException; + + if (ae != null) { + foreach (var ie in ae.InnerExceptions) + CollectExceptions (ie, exceptions); + } else { + exceptions.Add (ex); + } + } + + static bool ShowInternal (Exception e) + { + ProductException mte = (e as ProductException); + bool error = true; + + if (mte != null) { + error = mte.Error; + + if (!error && GetWarningLevel (mte.Code) == WarningLevel.Disable) + return false; // This is an ignored warning. + + Console.Error.WriteLine (mte.ToString ()); + + if (Verbosity > 1) + ShowInner (e); + + if (Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) + Console.Error.WriteLine (e.StackTrace); + } else { + Console.Error.WriteLine (e.ToString ()); + if (Verbosity > 1) + ShowInner (e); + if (Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) + Console.Error.WriteLine (e.StackTrace); + } + + return error; + } + + static void ShowInner (Exception e) + { + Exception ie = e.InnerException; + if (ie == null) + return; + + if (Verbosity > 3) { + Console.Error.WriteLine ("--- inner exception"); + Console.Error.WriteLine (ie); + Console.Error.WriteLine ("---"); + } else { + Console.Error.WriteLine ("\t{0}", ie.Message); + } + ShowInner (ie); + } + } +} diff --git a/objcgen/driver.cs b/objcgen/driver.cs index ed4b825..6f4f166 100644 --- a/objcgen/driver.cs +++ b/objcgen/driver.cs @@ -13,8 +13,18 @@ namespace Embeddinator { static class Driver { // TODO: use Mono.Options - // TODO: add errors.cs - public static int Main (string [] args) + + static int Main (string [] args) + { + try { + return Main2 (args); + } catch (Exception e) { + ErrorHelper.Show (e); + return 1; + } + } + + public static int Main2 (string [] args) { bool shared = true; // dylib diff --git a/objcgen/error.cs b/objcgen/error.cs new file mode 100644 index 0000000..771fa92 --- /dev/null +++ b/objcgen/error.cs @@ -0,0 +1,49 @@ +// Copyright 2011-2012, Xamarin Inc. All rights reserved, + +using System; +using System.Collections.Generic; + +// Error allocation: the errors are listed (and documented) in $(TOP)/docs/errors.md + +namespace Embeddinator +{ + public class EmbeddinatorException : Exception + { + internal const string PREFIX = "EM"; + + public EmbeddinatorException (int code, string message, params object [] args) : + this (code, true, message, args) + { + } + + public EmbeddinatorException (int code, bool error, string message, params object [] args) : + this (code, error, null, message, args) + { + } + + public EmbeddinatorException (int code, bool error, Exception innerException, string message, params object [] args) : + base (String.Format (message, args), innerException) + { + Code = code; + Error = error || ErrorHelper.GetWarningLevel (code) == ErrorHelper.WarningLevel.Error; + } + + public string FileName { get; set; } + + public int LineNumber { get; set; } + + public int Code { get; private set; } + + public bool Error { get; private set; } + + // http://blogs.msdn.com/b/msbuild/archive/2006/11/03/msbuild-visual-studio-aware-error-messages-and-message-formats.aspx + public override string ToString () + { + if (string.IsNullOrEmpty (FileName)) { + return String.Format ("{0} {3}{1:0000}: {2}", Error ? "error" : "warning", Code, Message, PREFIX); + } else { + return String.Format ("{3}({4}): {0} {5}{1:0000}: {2}", Error ? "error" : "warning", Code, Message, FileName, LineNumber, PREFIX); + } + } + } +} diff --git a/objcgen/objcgen.csproj b/objcgen/objcgen.csproj index c15df68..ba5e6a1 100644 --- a/objcgen/objcgen.csproj +++ b/objcgen/objcgen.csproj @@ -303,6 +303,8 @@ ikvm\VersionInfo.cs + + @@ -331,6 +333,9 @@ + + error.md + \ No newline at end of file diff --git a/objcgen/objcgenerator.cs b/objcgen/objcgenerator.cs index 304fccd..53b2d24 100644 --- a/objcgen/objcgenerator.cs +++ b/objcgen/objcgenerator.cs @@ -140,7 +140,7 @@ namespace ObjC { var setter = pi.GetSetMethod (); // FIXME: setter only is valid, even if discouraged, in .NET - we should create a SetX method if (getter == null && setter != null) - throw new NotSupportedException (); + throw ErrorHelper.CreateError (99, "Internal error. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)."); // TODO override with attribute ? e.g. [ObjC.Selector ("foo")] var name = CamelCase (pi.Name); @@ -204,7 +204,7 @@ namespace ObjC { implementation.WriteLine ($"\treturn *(({name}*)__unbox);"); break; default: - throw new NotSupportedException (); + throw ErrorHelper.CreateError (99, "Internal error. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)."); } } @@ -232,7 +232,7 @@ namespace ObjC { case TypeCode.Int32: return "int"; default: - throw new NotSupportedException (); + throw ErrorHelper.CreateError (99, "Internal error. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)."); } } diff --git a/tests/objcgentest/ObjCGeneratorTest.cs b/tests/objcgentest/ObjCGeneratorTest.cs index 0e01c4f..4e27b7c 100644 --- a/tests/objcgentest/ObjCGeneratorTest.cs +++ b/tests/objcgentest/ObjCGeneratorTest.cs @@ -29,5 +29,14 @@ namespace ObjCGeneratorTest { Assert.That (ObjCGenerator.GetTypeName (asm.GetType ("System.Int32")), Is.EqualTo ("int"), "int"); Assert.That (ObjCGenerator.GetTypeName (asm.GetType ("System.Object")), Is.EqualTo ("NSObject"), "object"); } + + [Test] + public void TypeMatchFailure () + { + var universe = new Universe (UniverseOptions.None); + var asm = universe.Load ("mscorlib.dll"); + + Assert.Throws (() => ObjCGenerator.GetTypeName (asm.GetType ("System.Char")), "char"); + } } }