// Copyright 2020, Microsoft Corp. All rights reserved, using System; using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; using Xamarin.Utils; namespace Xamarin.Bundler { public static partial class ErrorHelper { public static ApplePlatform Platform; internal static string Prefix { get { switch (Platform) { case ApplePlatform.iOS: case ApplePlatform.TVOS: case ApplePlatform.WatchOS: case ApplePlatform.MacCatalyst: case ApplePlatform.None: // Return "MT" by default instead of throwing an exception, because any exception here will most likely hide whatever other error we're trying to show. return "MT"; case ApplePlatform.MacOSX: return "MM"; default: // Do not use the ErrorHandler machinery, because it will probably end up recursing and eventually throwing a StackOverflowException. throw new InvalidOperationException ($"Unknown platform: {Platform}"); } } } public enum WarningLevel { Error = -1, Warning = 0, Disable = 1, } static Dictionary warning_levels; public static int Verbosity { get; set; } #pragma warning disable 649 public static Func IsExpectedException; public static Action ExitCallback; #pragma warning restore 649 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 void SetLocation (Application app, ProductException ex, MethodDefinition method, Instruction instruction = null) { if (!method.HasBody) return; if (instruction == null && method.Body.Instructions.Count == 0) return; if (instruction == null) instruction = method.Body.Instructions [0]; app.LoadSymbols (); if (!method.DebugInformation.HasSequencePoints) return; // Find the sequence point with the highest offset that is less than or equal to the instruction's offset SequencePoint seq = null; foreach (var pnt in method.DebugInformation.SequencePoints) { if (pnt.Offset > instruction.Offset) continue; if (seq != null && seq.Offset >= pnt.Offset) continue; seq = pnt; } if (seq == null) return; ex.FileName = seq.Document.Url; ex.LineNumber = seq.StartLine; } public static ProductException CreateError (Application app, int code, MemberReference member, string message, params object [] args) { return Create (app, code, true, null, member, null, message, args); } public static ProductException CreateError (Application app, int code, MethodDefinition location, string message, params object [] args) { return Create (app, code, true, null, location, null, message, args); } public static ProductException CreateError (Application app, int code, MethodDefinition location, Instruction instruction, string message, params object [] args) { return Create (app, code, true, null, location, instruction, message, args); } public static ProductException CreateError (Application app, int code, ICustomAttributeProvider provider, string message, params object [] args) { return Create (app, code, true, null, provider, null, message, args); } public static ProductException CreateError (Application app, int code, Exception innerException, MethodDefinition location, string message, params object [] args) { return Create (app, code, true, innerException, location, message, args); } public static ProductException CreateError (Application app, int code, Exception innerException, TypeReference location, string message, params object [] args) { return Create (app, code, true, innerException, location, message, args); } public static ProductException CreateError (Application app, int code, Exception innerException, ICustomAttributeProvider provider, string message, params object [] args) { return Create (app, code, true, innerException, provider, message, args); } public static ProductException CreateWarning (Application app, int code, MemberReference member, string message, params object [] args) { return Create (app, code, false, null, member, null, message, args); } public static ProductException CreateWarning (Application app, int code, MemberReference member, Instruction instruction, string message, params object [] args) { return Create (app, code, false, null, member, instruction, message, args); } public static ProductException CreateWarning (Application app, int code, MethodDefinition location, string message, params object [] args) { return Create (app, code, false, null, location, message, args); } public static ProductException CreateWarning (Application app, int code, ICustomAttributeProvider provider, string message, params object [] args) { return Create (app, code, false, null, provider, message, args); } public static ProductException CreateWarning (Application app, int code, Exception innerException, MethodDefinition location, string message, params object [] args) { return Create (app, code, false, innerException, location, message, args); } public static ProductException CreateWarning (Application app, int code, Exception innerException, MethodDefinition location, Instruction instruction, string message, params object [] args) { return Create (app, code, false, innerException, location, instruction, message, args); } public static ProductException CreateWarning (Application app, int code, Exception innerException, TypeReference location, string message, params object [] args) { return Create (app, code, false, innerException, location, message, args); } public static ProductException CreateWarning (Application app, int code, Exception innerException, ICustomAttributeProvider provider, string message, params object [] args) { return Create (app, code, false, innerException, provider, message, args); } public static ProductException Create (Application app, int code, bool error, Exception innerException, ICustomAttributeProvider provider, string message, params object [] args) { return Create (app, code, error, innerException, provider, null, message, args); } public static ProductException Create (Application app, int code, bool error, Exception innerException, ICustomAttributeProvider provider, Instruction instruction, string message, params object [] args) { if (provider is MemberReference member) { if (instruction != null) return Create (app, code, error, innerException, member, instruction, message, args); return Create (app, code, error, innerException, member, null, message, args); } if (provider is TypeReference type) return Create (app, code, error, innerException, type, message, args); return new ProductException (code, error, innerException, message, args); } public static ProductException Create (Application app, int code, bool error, Exception innerException, MemberReference member, Instruction instruction, string message, params object [] args) { var method = member as MethodReference; if (method == null) { var property = member as PropertyDefinition; if (property != null) { method = property.GetMethod; if (method == null) method = property.SetMethod; } } return Create (app, code, error, innerException, method == null ? null : method.Resolve (), instruction, message, args); } public static ProductException Create (Application app, int code, bool error, Exception innerException, MethodDefinition location, Instruction instruction, string message, params object [] args) { var e = new ProductException (code, error, innerException, message, args); if (location != null) SetLocation (app, e, location, instruction); return e; } public static ProductException Create (Application app, int code, bool error, Exception innerException, TypeReference location, string message, params object [] args) { var e = new ProductException (code, error, innerException, message, args); if (location != null) { var td = location.Resolve (); if (td.HasMethods) { foreach (var method in td.Methods) { if (!method.IsConstructor) continue; SetLocation (app, e, method); if (e.FileName != null) break; } } } return e; } 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)); } // Shows any warnings, and if there are any errors, throws an AggregateException. public static void ThrowIfErrors (IList exceptions) { if (exceptions?.Any () != true) return; // Separate warnings from errors var grouped = exceptions.GroupBy ((v) => (v as ProductException)?.Error == false); var warnings = grouped.SingleOrDefault ((v) => v.Key); if (warnings?.Any () == true) Show (warnings); var errors = grouped.SingleOrDefault ((v) => !v.Key); if (errors?.Any () == true) throw new AggregateException (errors); } public static void Show (IEnumerable list) { var exceptions = CollectExceptions (list); bool error = false; foreach (var ex in exceptions) error |= ShowInternal (ex); if (error) Exit (1); } public static void Show (Exception e) { Show (new Exception [] { e }); } static void Exit (int exitCode) { if (ExitCallback != null) ExitCallback (exitCode); Environment.Exit (exitCode); } 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 ()); ShowInner (e); if (Verbosity > 2 && !string.IsNullOrEmpty (e.StackTrace)) Console.Error.WriteLine (e.StackTrace); } else if (IsExpectedException == null || !IsExpectedException (e)) { Console.Error.WriteLine ("error " + Prefix + "0000: Unexpected error - Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new"); Console.Error.WriteLine (e.ToString ()); } else { Console.Error.WriteLine (e.ToString ()); 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 if (Verbosity > 0 || ie is ProductException) { Console.Error.WriteLine ("\t{0}", ie.Message); } ShowInner (ie); } } }