// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // This file defines an internal class used to throw exceptions in BCL code. // The main purpose is to reduce code size. // // The old way to throw an exception generates quite a lot IL code and assembly code. // Following is an example: // C# source // throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); // IL code: // IL_0003: ldstr "key" // IL_0008: ldstr "ArgumentNull_Key" // IL_000d: call string System.Environment::GetResourceString(string) // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string) // IL_0017: throw // which is 21bytes in IL. // // So we want to get rid of the ldstr and call to Environment.GetResource in IL. // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the // argument name and resource name in a small integer. The source code will be changed to // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key); // // The IL code will be 7 bytes. // IL_0008: ldc.i4.4 // IL_0009: ldc.i4.4 // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument) // IL_000f: ldarg.0 // // This will also reduce the Jitted code size a lot. // // It is very important we do this for generic classes because we can easily generate the same code // multiple times for different instantiation. // using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace System { [StackTraceHidden] internal static class ThrowHelper { internal static void ThrowArrayTypeMismatchException() { throw new ArrayTypeMismatchException(); } internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { throw new ArgumentException(SR.Format(SR.Argument_InvalidTypeWithPointersNotSupported, targetType)); } internal static void ThrowIndexOutOfRangeException() { throw new IndexOutOfRangeException(); } internal static void ThrowArgumentOutOfRangeException() { throw new ArgumentOutOfRangeException(); } internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw new ArgumentOutOfRangeException(GetArgumentName(argument)); } private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) { return new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceString(resource)); } internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) { throw GetArgumentOutOfRangeException(argument, resource); } internal static void ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index() { throw GetArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); } internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count() { throw GetArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count); } internal static void ThrowArgumentException_DestinationTooShort() { throw new ArgumentException(SR.Argument_DestinationTooShort); } internal static void ThrowArgumentException_OverlapAlignmentMismatch() { throw new ArgumentException(SR.Argument_OverlapAlignmentMismatch); } internal static void ThrowArgumentException_CannotExtractScalar(ExceptionArgument argument) { throw GetArgumentException(ExceptionResource.Argument_CannotExtractScalar, argument); } internal static void ThrowArgumentOutOfRange_IndexException() { throw GetArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); } internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException() { throw GetArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException() { throw GetArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) { return new ArgumentException(SR.Format(SR.Arg_WrongType, key, targetType), nameof(key)); } internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) { throw GetWrongKeyTypeArgumentException(key, targetType); } private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) { return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); } internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) { throw GetWrongValueTypeArgumentException(value, targetType); } private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) { return new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); } internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) { throw GetAddingDuplicateWithKeyArgumentException(key); } private static KeyNotFoundException GetKeyNotFoundException(object key) { throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); } internal static void ThrowKeyNotFoundException(object key) { throw GetKeyNotFoundException(key); } internal static void ThrowArgumentException(ExceptionResource resource) { throw new ArgumentException(GetResourceString(resource)); } private static ArgumentException GetArgumentException(ExceptionResource resource, ExceptionArgument argument) { return new ArgumentException(GetResourceString(resource), GetArgumentName(argument)); } internal static void ThrowArgumentException(ExceptionResource resource, ExceptionArgument argument) { throw GetArgumentException(resource, argument); } internal static void ThrowAggregateException(List exceptions) { throw new AggregateException(exceptions); } internal static void ThrowArgumentException_Argument_InvalidArrayType() { throw new ArgumentException(SR.Argument_InvalidArrayType); } internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw new ArgumentNullException(GetArgumentName(argument)); } internal static void ThrowInvalidOperationException(ExceptionResource resource) { throw new InvalidOperationException(GetResourceString(resource)); } internal static void ThrowInvalidOperationException_OutstandingReferences() { throw new InvalidOperationException(SR.Memory_OutstandingReferences); } internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() { throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); } internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() { throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); } internal static void ThrowInvalidOperationException_InvalidOperation_EnumNotStarted() { throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted); } internal static void ThrowInvalidOperationException_InvalidOperation_EnumEnded() { throw new InvalidOperationException(SR.InvalidOperation_EnumEnded); } internal static void ThrowInvalidOperationException_InvalidOperation_NoValue() { throw new InvalidOperationException(SR.InvalidOperation_NoValue); } internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() { throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported); } internal static void ThrowSerializationException(ExceptionResource resource) { throw new SerializationException(GetResourceString(resource)); } internal static void ThrowObjectDisposedException(ExceptionResource resource) { throw new ObjectDisposedException(null, GetResourceString(resource)); } internal static void ThrowNotSupportedException() { throw new NotSupportedException(); } internal static void ThrowNotSupportedException(ExceptionResource resource) { throw new NotSupportedException(GetResourceString(resource)); } private static Exception GetArraySegmentCtorValidationFailedException(Array array, int offset, int count) { if (array == null) return new ArgumentNullException(nameof(array)); if (offset < 0) return new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum); if (count < 0) return new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum); Debug.Assert(array.Length - offset < count); return new ArgumentException(SR.Argument_InvalidOffLen); } internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array array, int offset, int count) { throw GetArraySegmentCtorValidationFailedException(array, offset, count); } internal static void ThrowFormatException_BadFormatSpecifier() { throw new FormatException(SR.Argument_BadFormatSpecifier); } internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { throw new ArgumentOutOfRangeException("precision", SR.Format(SR.Argument_PrecisionTooLarge, StandardFormat.MaxPrecision)); } internal static void ThrowArgumentOutOfRangeException_SymbolDoesNotFit() { throw new ArgumentOutOfRangeException("symbol", SR.Argument_BadFormatSpecifier); } // Allow nulls for reference types and Nullable, but not for value types. // Aggressively inline so the jit evaluates the if in place and either drops the call altogether // Or just leaves null test and call to the Non-returning ThrowHelper.ThrowArgumentNullException [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void IfNullAndNullsAreIllegalThenThrow(object value, ExceptionArgument argName) { // Note that default(T) is not equal to null for value types except when T is Nullable. if (!(default(T) == null) && value == null) ThrowHelper.ThrowArgumentNullException(argName); } private static string GetArgumentName(ExceptionArgument argument) { switch (argument) { case ExceptionArgument.obj: return "obj"; case ExceptionArgument.dictionary: return "dictionary"; case ExceptionArgument.array: return "array"; case ExceptionArgument.info: return "info"; case ExceptionArgument.key: return "key"; case ExceptionArgument.text: return "text"; case ExceptionArgument.values: return "values"; case ExceptionArgument.value: return "value"; case ExceptionArgument.startIndex: return "startIndex"; case ExceptionArgument.task: return "task"; case ExceptionArgument.ch: return "ch"; case ExceptionArgument.s: return "s"; case ExceptionArgument.input: return "input"; case ExceptionArgument.ownedMemory: return "ownedMemory"; case ExceptionArgument.list: return "list"; case ExceptionArgument.index: return "index"; case ExceptionArgument.capacity: return "capacity"; case ExceptionArgument.collection: return "collection"; case ExceptionArgument.item: return "item"; case ExceptionArgument.converter: return "converter"; case ExceptionArgument.match: return "match"; case ExceptionArgument.count: return "count"; case ExceptionArgument.action: return "action"; case ExceptionArgument.comparison: return "comparison"; case ExceptionArgument.exceptions: return "exceptions"; case ExceptionArgument.exception: return "exception"; case ExceptionArgument.pointer: return "pointer"; case ExceptionArgument.start: return "start"; case ExceptionArgument.format: return "format"; case ExceptionArgument.culture: return "culture"; case ExceptionArgument.comparer: return "comparer"; case ExceptionArgument.comparable: return "comparable"; case ExceptionArgument.source: return "source"; case ExceptionArgument.state: return "state"; case ExceptionArgument.length: return "length"; case ExceptionArgument.comparisonType: return "comparisonType"; case ExceptionArgument.manager: return "manager"; case ExceptionArgument.sourceBytesToCopy: return "sourceBytesToCopy"; case ExceptionArgument.callBack: return "callBack"; case ExceptionArgument.creationOptions: return "creationOptions"; case ExceptionArgument.function: return "function"; case ExceptionArgument.scheduler: return "scheduler"; case ExceptionArgument.continuationAction: return "continuationAction"; case ExceptionArgument.continuationFunction: return "continuationFunction"; case ExceptionArgument.tasks: return "tasks"; case ExceptionArgument.asyncResult: return "asyncResult"; case ExceptionArgument.beginMethod: return "beginMethod"; case ExceptionArgument.endMethod: return "endMethod"; case ExceptionArgument.endFunction: return "endFunction"; case ExceptionArgument.cancellationToken: return "cancellationToken"; case ExceptionArgument.continuationOptions: return "continuationOptions"; case ExceptionArgument.delay: return "delay"; case ExceptionArgument.millisecondsDelay: return "millisecondsDelay"; case ExceptionArgument.millisecondsTimeout: return "millisecondsTimeout"; case ExceptionArgument.stateMachine: return "stateMachine"; case ExceptionArgument.timeout: return "timeout"; default: Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); return ""; } } private static string GetResourceString(ExceptionResource resource) { switch (resource) { case ExceptionResource.Argument_InvalidArgumentForComparison: return SR.Argument_InvalidArgumentForComparison; case ExceptionResource.ArgumentOutOfRange_Index: return SR.ArgumentOutOfRange_Index; case ExceptionResource.ArgumentOutOfRange_Count: return SR.ArgumentOutOfRange_Count; case ExceptionResource.Arg_ArrayPlusOffTooSmall: return SR.Arg_ArrayPlusOffTooSmall; case ExceptionResource.NotSupported_ReadOnlyCollection: return SR.NotSupported_ReadOnlyCollection; case ExceptionResource.Arg_RankMultiDimNotSupported: return SR.Arg_RankMultiDimNotSupported; case ExceptionResource.Arg_NonZeroLowerBound: return SR.Arg_NonZeroLowerBound; case ExceptionResource.ArgumentOutOfRange_ListInsert: return SR.ArgumentOutOfRange_ListInsert; case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: return SR.ArgumentOutOfRange_NeedNonNegNum; case ExceptionResource.ArgumentOutOfRange_SmallCapacity: return SR.ArgumentOutOfRange_SmallCapacity; case ExceptionResource.Argument_InvalidOffLen: return SR.Argument_InvalidOffLen; case ExceptionResource.Argument_CannotExtractScalar: return SR.Argument_CannotExtractScalar; case ExceptionResource.ArgumentOutOfRange_BiggerThanCollection: return SR.ArgumentOutOfRange_BiggerThanCollection; case ExceptionResource.Serialization_MissingKeys: return SR.Serialization_MissingKeys; case ExceptionResource.Serialization_NullKey: return SR.Serialization_NullKey; case ExceptionResource.NotSupported_KeyCollectionSet: return SR.NotSupported_KeyCollectionSet; case ExceptionResource.NotSupported_ValueCollectionSet: return SR.NotSupported_ValueCollectionSet; case ExceptionResource.InvalidOperation_NullArray: return SR.InvalidOperation_NullArray; case ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted: return SR.TaskT_TransitionToFinal_AlreadyCompleted; case ExceptionResource.TaskCompletionSourceT_TrySetException_NullException: return SR.TaskCompletionSourceT_TrySetException_NullException; case ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions: return SR.TaskCompletionSourceT_TrySetException_NoExceptions; case ExceptionResource.NotSupported_StringComparison: return SR.NotSupported_StringComparison; case ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported: return SR.ConcurrentCollection_SyncRoot_NotSupported; case ExceptionResource.Task_MultiTaskContinuation_NullTask: return SR.Task_MultiTaskContinuation_NullTask; case ExceptionResource.InvalidOperation_WrongAsyncResultOrEndCalledMultiple: return SR.InvalidOperation_WrongAsyncResultOrEndCalledMultiple; case ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList: return SR.Task_MultiTaskContinuation_EmptyTaskList; case ExceptionResource.Task_Start_TaskCompleted: return SR.Task_Start_TaskCompleted; case ExceptionResource.Task_Start_Promise: return SR.Task_Start_Promise; case ExceptionResource.Task_Start_ContinuationTask: return SR.Task_Start_ContinuationTask; case ExceptionResource.Task_Start_AlreadyStarted: return SR.Task_Start_AlreadyStarted; case ExceptionResource.Task_RunSynchronously_Continuation: return SR.Task_RunSynchronously_Continuation; case ExceptionResource.Task_RunSynchronously_Promise: return SR.Task_RunSynchronously_Promise; case ExceptionResource.Task_RunSynchronously_TaskCompleted: return SR.Task_RunSynchronously_TaskCompleted; case ExceptionResource.Task_RunSynchronously_AlreadyStarted: return SR.Task_RunSynchronously_AlreadyStarted; case ExceptionResource.AsyncMethodBuilder_InstanceNotInitialized: return SR.AsyncMethodBuilder_InstanceNotInitialized; case ExceptionResource.Task_ContinueWith_ESandLR: return SR.Task_ContinueWith_ESandLR; case ExceptionResource.Task_ContinueWith_NotOnAnything: return SR.Task_ContinueWith_NotOnAnything; case ExceptionResource.Task_Delay_InvalidDelay: return SR.Task_Delay_InvalidDelay; case ExceptionResource.Task_Delay_InvalidMillisecondsDelay: return SR.Task_Delay_InvalidMillisecondsDelay; case ExceptionResource.Task_Dispose_NotCompleted: return SR.Task_Dispose_NotCompleted; case ExceptionResource.Task_ThrowIfDisposed: return SR.Task_ThrowIfDisposed; case ExceptionResource.Task_WaitMulti_NullTask: return SR.Task_WaitMulti_NullTask; default: Debug.Assert(false, "The enum value is not defined, please check the ExceptionResource Enum."); return ""; } } } // // The convention for this enum is using the argument name as the enum name // internal enum ExceptionArgument { obj, dictionary, array, info, key, text, values, value, startIndex, task, ch, s, input, ownedMemory, list, index, capacity, collection, item, converter, match, count, action, comparison, exceptions, exception, pointer, start, format, culture, comparer, comparable, source, state, length, comparisonType, manager, sourceBytesToCopy, callBack, creationOptions, function, scheduler, continuationAction, continuationFunction, tasks, asyncResult, beginMethod, endMethod, endFunction, cancellationToken, continuationOptions, delay, millisecondsDelay, millisecondsTimeout, stateMachine, timeout, } // // The convention for this enum is using the resource name as the enum name // internal enum ExceptionResource { Argument_InvalidArgumentForComparison, ArgumentOutOfRange_Index, ArgumentOutOfRange_Count, Arg_ArrayPlusOffTooSmall, NotSupported_ReadOnlyCollection, Arg_RankMultiDimNotSupported, Arg_NonZeroLowerBound, ArgumentOutOfRange_ListInsert, ArgumentOutOfRange_NeedNonNegNum, ArgumentOutOfRange_SmallCapacity, Argument_InvalidOffLen, Argument_CannotExtractScalar, ArgumentOutOfRange_BiggerThanCollection, Serialization_MissingKeys, Serialization_NullKey, NotSupported_KeyCollectionSet, NotSupported_ValueCollectionSet, InvalidOperation_NullArray, TaskT_TransitionToFinal_AlreadyCompleted, TaskCompletionSourceT_TrySetException_NullException, TaskCompletionSourceT_TrySetException_NoExceptions, NotSupported_StringComparison, ConcurrentCollection_SyncRoot_NotSupported, Task_MultiTaskContinuation_NullTask, InvalidOperation_WrongAsyncResultOrEndCalledMultiple, Task_MultiTaskContinuation_EmptyTaskList, Task_Start_TaskCompleted, Task_Start_Promise, Task_Start_ContinuationTask, Task_Start_AlreadyStarted, Task_RunSynchronously_Continuation, Task_RunSynchronously_Promise, Task_RunSynchronously_TaskCompleted, Task_RunSynchronously_AlreadyStarted, AsyncMethodBuilder_InstanceNotInitialized, Task_ContinueWith_ESandLR, Task_ContinueWith_NotOnAnything, Task_Delay_InvalidDelay, Task_Delay_InvalidMillisecondsDelay, Task_Dispose_NotCompleted, Task_ThrowIfDisposed, Task_WaitMulti_NullTask, } }