// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Globalization; using System.Threading; #if BUILDINGAPPXTASKS namespace Microsoft.Build.AppxPackage.Shared #else namespace Microsoft.Build.Shared #endif { /// /// This class contains methods that are useful for error checking and validation. /// internal static class ErrorUtilities { /// /// Emergency escape hatch. If a customer hits a bug in the shipped product causing an internal exception, /// and fortuitously it happens that ignoring the VerifyThrow allows execution to continue in a reasonable way, /// then we can give them this undocumented environment variable as an immediate workaround. /// private static readonly bool s_throwExceptions = String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDDONOTTHROWINTERNAL")); private static readonly bool s_enableMSBuildDebugTracing = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDENABLEDEBUGTRACING")); #region DebugTracing public static void DebugTraceMessage(string category, string formatstring, params object[] parameters) { if (s_enableMSBuildDebugTracing) { if (parameters != null) { Trace.WriteLine(String.Format(CultureInfo.CurrentCulture, formatstring, parameters), category); } else { Trace.WriteLine(formatstring, category); } } } #endregion #if !BUILDINGAPPXTASKS #region VerifyThrow -- for internal errors /// /// Throws InternalErrorException. /// This is only for situations that would mean that there is a bug in MSBuild itself. /// internal static void ThrowInternalError(string message, params object[] args) { if (s_throwExceptions) { throw new InternalErrorException(ResourceUtilities.FormatString(message, args)); } } /// /// Throws InternalErrorException. /// This is only for situations that would mean that there is a bug in MSBuild itself. /// internal static void ThrowInternalError(string message, Exception innerException, params object[] args) { if (s_throwExceptions) { throw new InternalErrorException(ResourceUtilities.FormatString(message, args), innerException); } } /// /// Throws InternalErrorException. /// Indicates the code path followed should not have been possible. /// This is only for situations that would mean that there is a bug in MSBuild itself. /// internal static void ThrowInternalErrorUnreachable() { if (s_throwExceptions) { throw new InternalErrorException("Unreachable?"); } } /// /// Throws InternalErrorException. /// Indicates the code path followed should not have been possible. /// This is only for situations that would mean that there is a bug in MSBuild itself. /// internal static void VerifyThrowInternalErrorUnreachable(bool condition) { if (s_throwExceptions && !condition) { throw new InternalErrorException("Unreachable?"); } } /// /// Throws InternalErrorException. /// Indicates the code path followed should not have been possible. /// This is only for situations that would mean that there is a bug in MSBuild itself. /// internal static void ThrowIfTypeDoesNotImplementToString(object param) { #if DEBUG // Check it has a real implementation of ToString() if (String.Equals(param.GetType().ToString(), param.ToString(), StringComparison.Ordinal)) { ErrorUtilities.ThrowInternalError("This type does not implement ToString() properly {0}", param.GetType().FullName); } #endif } /// /// Helper to throw an InternalErrorException when the specified parameter is null. /// This should be used ONLY if this would indicate a bug in MSBuild rather than /// anything caused by user action. /// /// The value of the argument. /// Parameter that should not be null internal static void VerifyThrowInternalNull(object parameter, string parameterName) { if (parameter == null) { ThrowInternalError("{0} unexpectedly null", parameterName); } } /// /// Helper to throw an InternalErrorException when a lock on the specified object is not already held. /// This should be used ONLY if this would indicate a bug in MSBuild rather than /// anything caused by user action. /// /// The object that should already have been used as a lock. internal static void VerifyThrowInternalLockHeld(object locker) { #if !CLR2COMPATIBILITY if (!Monitor.IsEntered(locker)) { ThrowInternalError("Lock should already have been taken"); } #endif } /// /// Helper to throw an InternalErrorException when the specified parameter is null or zero length. /// This should be used ONLY if this would indicate a bug in MSBuild rather than /// anything caused by user action. /// /// The value of the argument. /// Parameter that should not be null or zero length internal static void VerifyThrowInternalLength(string parameterValue, string parameterName) { VerifyThrowInternalNull(parameterValue, parameterName); if (parameterValue.Length == 0) { ThrowInternalError("{0} unexpectedly empty", parameterName); } } public static void VerifyThrowInternalLength(T[] parameterValue, string parameterName) { VerifyThrowInternalNull(parameterValue, parameterName); if (parameterValue.Length == 0) { ThrowInternalError("{0} unexpectedly empty", parameterName); } } /// /// Helper to throw an InternalErrorException when the specified parameter is not a rooted path. /// This should be used ONLY if this would indicate a bug in MSBuild rather than /// anything caused by user action. /// /// Parameter that should be a rooted path internal static void VerifyThrowInternalRooted(string value) { if (!Path.IsPathRooted(value)) { ThrowInternalError("{0} unexpectedly not a rooted path", value); } } /// /// This method should be used in places where one would normally put /// an "assert". It should be used to validate that our assumptions are /// true, where false would indicate that there must be a bug in our /// code somewhere. This should not be used to throw errors based on bad /// user input or anything that the user did wrong. /// /// /// internal static void VerifyThrow ( bool condition, string unformattedMessage ) { if (!condition) { // PERF NOTE: explicitly passing null for the arguments array // prevents memory allocation ThrowInternalError(unformattedMessage, null, null); } } /// /// Overload for one string format argument. /// /// /// /// internal static void VerifyThrow ( bool condition, string unformattedMessage, object arg0 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInternalError() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInternalError(unformattedMessage, arg0); } } /// /// Overload for two string format arguments. /// /// /// /// /// internal static void VerifyThrow ( bool condition, string unformattedMessage, object arg0, object arg1 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInternalError() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInternalError(unformattedMessage, arg0, arg1); } } /// /// Overload for three string format arguments. /// /// /// /// /// /// internal static void VerifyThrow ( bool condition, string unformattedMessage, object arg0, object arg1, object arg2 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInternalError() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInternalError(unformattedMessage, arg0, arg1, arg2); } } /// /// Overload for four string format arguments. /// /// /// /// /// /// /// internal static void VerifyThrow ( bool condition, string unformattedMessage, object arg0, object arg1, object arg2, object arg3 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInternalError() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInternalError(unformattedMessage, arg0, arg1, arg2, arg3); } } #endregion #region VerifyThrowInvalidOperation /// /// Throws an InvalidOperationException with the specified resource string /// /// Resource to use in the exception /// Formatting args. internal static void ThrowInvalidOperation(string resourceName, params object[] args) { #if DEBUG ResourceUtilities.VerifyResourceStringExists(resourceName); #endif if (s_throwExceptions) { throw new InvalidOperationException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword(resourceName, args)); } } /// /// Throws an InvalidOperationException if the given condition is false. /// /// /// internal static void VerifyThrowInvalidOperation ( bool condition, string resourceName ) { if (!condition) { // PERF NOTE: explicitly passing null for the arguments array // prevents memory allocation ThrowInvalidOperation(resourceName, null); } } /// /// Overload for one string format argument. /// /// /// /// internal static void VerifyThrowInvalidOperation ( bool condition, string resourceName, object arg0 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInvalidOperation() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInvalidOperation(resourceName, arg0); } } /// /// Overload for two string format arguments. /// /// /// /// /// internal static void VerifyThrowInvalidOperation ( bool condition, string resourceName, object arg0, object arg1 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInvalidOperation() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInvalidOperation(resourceName, arg0, arg1); } } /// /// Overload for three string format arguments. /// /// /// /// /// /// internal static void VerifyThrowInvalidOperation ( bool condition, string resourceName, object arg0, object arg1, object arg2 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInvalidOperation() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInvalidOperation(resourceName, arg0, arg1, arg2); } } /// /// Overload for four string format arguments. /// /// /// /// /// /// /// internal static void VerifyThrowInvalidOperation ( bool condition, string resourceName, object arg0, object arg1, object arg2, object arg3 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowInvalidOperation() method, because that method always // allocates memory for its variable array of arguments if (!condition) { ThrowInvalidOperation(resourceName, arg0, arg1, arg2, arg3); } } #endregion #region VerifyThrowArgument /// /// Throws an ArgumentException that can include an inner exception. /// /// PERF WARNING: calling a method that takes a variable number of arguments /// is expensive, because memory is allocated for the array of arguments -- do /// not call this method repeatedly in performance-critical scenarios /// internal static void ThrowArgument ( string resourceName, params object[] args ) { ThrowArgument(null, resourceName, args); } /// /// Throws an ArgumentException that can include an inner exception. /// /// PERF WARNING: calling a method that takes a variable number of arguments /// is expensive, because memory is allocated for the array of arguments -- do /// not call this method repeatedly in performance-critical scenarios /// /// /// This method is thread-safe. /// /// Can be null. /// /// private static void ThrowArgument ( Exception innerException, string resourceName, params object[] args ) { #if DEBUG ResourceUtilities.VerifyResourceStringExists(resourceName); #endif if (s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword(resourceName, args), innerException); } } /// /// Throws an ArgumentException if the given condition is false. /// /// This method is thread-safe. /// /// internal static void VerifyThrowArgument ( bool condition, string resourceName ) { VerifyThrowArgument(condition, null, resourceName); } /// /// Overload for one string format argument. /// /// This method is thread-safe. /// /// /// internal static void VerifyThrowArgument ( bool condition, string resourceName, object arg0 ) { VerifyThrowArgument(condition, null, resourceName, arg0); } /// /// Overload for two string format arguments. /// /// This method is thread-safe. /// /// /// /// internal static void VerifyThrowArgument ( bool condition, string resourceName, object arg0, object arg1 ) { VerifyThrowArgument(condition, null, resourceName, arg0, arg1); } /// /// Overload for three string format arguments. /// /// This method is thread-safe. internal static void VerifyThrowArgument ( bool condition, string resourceName, object arg0, object arg1, object arg2 ) { VerifyThrowArgument(condition, null, resourceName, arg0, arg1, arg2); } /// /// Overload for four string format arguments. /// /// This method is thread-safe. internal static void VerifyThrowArgument ( bool condition, string resourceName, object arg0, object arg1, object arg2, object arg3 ) { VerifyThrowArgument(condition, null, resourceName, arg0, arg1, arg2, arg3); } /// /// Throws an ArgumentException that includes an inner exception, if /// the given condition is false. /// /// This method is thread-safe. /// /// Can be null. /// internal static void VerifyThrowArgument ( bool condition, Exception innerException, string resourceName ) { if (!condition) { // PERF NOTE: explicitly passing null for the arguments array // prevents memory allocation ThrowArgument(innerException, resourceName, null); } } /// /// Overload for one string format argument. /// /// This method is thread-safe. /// /// /// /// internal static void VerifyThrowArgument ( bool condition, Exception innerException, string resourceName, object arg0 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowArgument() method, because that method always allocates // memory for its variable array of arguments if (!condition) { ThrowArgument(innerException, resourceName, arg0); } } /// /// Overload for two string format arguments. /// /// This method is thread-safe. /// /// /// /// /// internal static void VerifyThrowArgument ( bool condition, Exception innerException, string resourceName, object arg0, object arg1 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowArgument() method, because that method always allocates // memory for its variable array of arguments if (!condition) { ThrowArgument(innerException, resourceName, arg0, arg1); } } /// /// Overload for three string format arguments. /// /// This method is thread-safe. internal static void VerifyThrowArgument ( bool condition, Exception innerException, string resourceName, object arg0, object arg1, object arg2 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowArgument() method, because that method always allocates // memory for its variable array of arguments if (!condition) { ThrowArgument(innerException, resourceName, arg0, arg1, arg2); } } /// /// Overload for four string format arguments. /// /// This method is thread-safe. internal static void VerifyThrowArgument ( bool condition, Exception innerException, string resourceName, object arg0, object arg1, object arg2, object arg3 ) { // PERF NOTE: check the condition here instead of pushing it into // the ThrowArgument() method, because that method always allocates // memory for its variable array of arguments if (!condition) { ThrowArgument(innerException, resourceName, arg0, arg1, arg2, arg3); } } #endregion #region VerifyThrowArgumentXXX /// /// Throws an argument out of range exception. /// internal static void ThrowArgumentOutOfRange(string parameterName) { if (s_throwExceptions) { throw new ArgumentOutOfRangeException(parameterName); } } /// /// Throws an ArgumentOutOfRangeException using the given parameter name /// if the condition is false. /// internal static void VerifyThrowArgumentOutOfRange(bool condition, string parameterName) { if (!condition) { ThrowArgumentOutOfRange(parameterName); } } /// /// Throws an ArgumentNullException if the given string parameter is null /// and ArgumentException if it has zero length. /// /// /// internal static void VerifyThrowArgumentLength(string parameter, string parameterName) { VerifyThrowArgumentNull(parameter, parameterName); if (parameter.Length == 0 && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParameterCannotHaveZeroLength", parameterName)); } } #if !CLR2COMPATIBILITY /// /// Throws an ArgumentNullException if the given collection is null /// and ArgumentException if it has zero length. /// /// /// internal static void VerifyThrowArgumentLength(IReadOnlyCollection parameter, string parameterName) { VerifyThrowArgumentNull(parameter, parameterName); if (parameter.Count == 0 && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParameterCannotHaveZeroLength", parameterName)); } } /// /// Throws an ArgumentException if the given collection is not null but of zero length. /// /// /// internal static void VerifyThrowArgumentLengthIfNotNull(IReadOnlyCollection parameter, string parameterName) { if (parameter?.Count == 0 && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParameterCannotHaveZeroLength", parameterName)); } } #endif /// /// Throws an ArgumentNullException if the given string parameter is null /// and ArgumentException if it has zero length. /// /// /// internal static void VerifyThrowArgumentInvalidPath(string parameter, string parameterName) { VerifyThrowArgumentNull(parameter, parameterName); if (FileUtilities.PathIsInvalid(parameter) && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParameterCannotHaveInvalidPathChars", parameterName, parameter)); } } /// /// Throws an ArgumentException if the string has zero length, unless it is /// null, in which case no exception is thrown. /// internal static void VerifyThrowArgumentLengthIfNotNull(string parameter, string parameterName) { if (parameter?.Length == 0 && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParameterCannotHaveZeroLength", parameterName)); } } /// /// Throws an ArgumentNullException if the given parameter is null. /// /// This method is thread-safe. /// /// internal static void VerifyThrowArgumentNull(object parameter, string parameterName) { VerifyThrowArgumentNull(parameter, parameterName, "Shared.ParameterCannotBeNull"); } /// /// Throws an ArgumentNullException if the given parameter is null. /// /// This method is thread-safe. internal static void VerifyThrowArgumentNull(object parameter, string parameterName, string resourceName) { if (parameter == null && s_throwExceptions) { // Most ArgumentNullException overloads append its own rather clunky multi-line message. // So use the one overload that doesn't. throw new ArgumentNullException( ResourceUtilities.FormatResourceStringStripCodeAndKeyword(resourceName, parameterName), (Exception)null); } } /// /// Verifies the given arrays are not null and have the same length /// /// /// /// /// internal static void VerifyThrowArgumentArraysSameLength(Array parameter1, Array parameter2, string parameter1Name, string parameter2Name) { VerifyThrowArgumentNull(parameter1, parameter1Name); VerifyThrowArgumentNull(parameter2, parameter2Name); if (parameter1.Length != parameter2.Length && s_throwExceptions) { throw new ArgumentException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.ParametersMustHaveTheSameLength", parameter1Name, parameter2Name)); } } #endregion #region VerifyThrowObjectDisposed internal static void VerifyThrowObjectDisposed(bool condition, string objectName) { { if (s_throwExceptions && !condition) { throw new ObjectDisposedException(objectName); } } } #endregion #endif } }