--- /dev/null
+// 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<Exception> 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<U>, 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<T>(object value, ExceptionArgument argName)
+ {
+ // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
+ 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.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
+ {
+ 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,
+ }
+}