2 // This file defines an internal class used to throw exceptions in BCL code.
3 // The main purpose is to reduce code size.
5 // The old way to throw an exception generates quite a lot IL code and assembly code.
6 // Following is an example:
8 // throw new ArgumentNullException("key", SR.GetString("ArgumentNull_Key"));
10 // IL_0003: ldstr "key"
11 // IL_0008: ldstr "ArgumentNull_Key"
12 // IL_000d: call string System.Environment::GetResourceString(string)
13 // IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string)
15 // which is 21bytes in IL.
17 // So we want to get rid of the ldstr and call to Environment.GetResource in IL.
18 // In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the
19 // argument name and resource name in a small integer. The source code will be changed to
20 // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key);
22 // The IL code will be 7 bytes.
25 // IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)
28 // This will also reduce the Jitted code size a lot.
30 // It is very important we do this for generic classes because we can easily generate the same code
31 // multiple times for different instantiation.
34 // Jit will generates the code to throw exception at the end of a method, thus we can reduce working
35 // set if the user code will never throw an exception. However Jit doesn't know anything about the
36 // methods in ThrowHelper, so it will not moves the instructions to the end.
37 // This is not a problem for ngened code because we will probably move the code based on profile data(hopefully.)
39 // For jitted code, it doesn't make much difference. The only advantage of moving the code to the end is to
40 // improve cache locality. Patrick pointed out this doesn't make much different on newer processor like P4.
45 using System.Runtime.Serialization;
48 using System.Diagnostics;
49 internal static class ThrowHelper {
50 internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) {
51 throw new ArgumentException(SR.GetString(SR.Arg_WrongType, key, targetType), "key");
54 internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) {
55 throw new ArgumentException(SR.GetString(SR.Arg_WrongType, value, targetType), "value");
58 internal static void ThrowKeyNotFoundException() {
59 throw new System.Collections.Generic.KeyNotFoundException();
62 internal static void ThrowArgumentException(ExceptionResource resource) {
63 throw new ArgumentException(SR.GetString(GetResourceName(resource)));
66 internal static void ThrowArgumentNullException(ExceptionArgument argument) {
67 throw new ArgumentNullException(GetArgumentName(argument));
70 internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) {
71 throw new ArgumentOutOfRangeException(GetArgumentName(argument));
74 internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) {
75 throw new ArgumentOutOfRangeException(GetArgumentName(argument), SR.GetString(GetResourceName(resource)));
78 internal static void ThrowInvalidOperationException(ExceptionResource resource) {
79 throw new InvalidOperationException(SR.GetString(GetResourceName(resource)));
83 internal static void ThrowSerializationException(ExceptionResource resource) {
84 throw new SerializationException(SR.GetString(GetResourceName(resource)));
88 internal static void ThrowNotSupportedException(ExceptionResource resource) {
89 throw new NotSupportedException(SR.GetString(GetResourceName(resource)));
92 // Allow nulls for reference types and Nullable<U>, but not for value types.
93 internal static void IfNullAndNullsAreIllegalThenThrow<T>(object value, ExceptionArgument argName) {
94 // Note that default(T) is not equal to null for value types except when T is Nullable<U>.
95 if (value == null && !(default(T) == null))
96 ThrowHelper.ThrowArgumentNullException(argName);
100 // This function will convert an ExceptionArgument enum value to the argument name string.
102 internal static string GetArgumentName(ExceptionArgument argument) {
103 string argumentName = null;
106 case ExceptionArgument.array:
107 argumentName = "array";
110 case ExceptionArgument.arrayIndex:
111 argumentName = "arrayIndex";
114 case ExceptionArgument.capacity:
115 argumentName = "capacity";
118 case ExceptionArgument.collection:
119 argumentName = "collection";
122 case ExceptionArgument.converter:
123 argumentName = "converter";
126 case ExceptionArgument.count:
127 argumentName = "count";
130 case ExceptionArgument.dictionary:
131 argumentName = "dictionary";
134 case ExceptionArgument.index:
135 argumentName = "index";
138 case ExceptionArgument.info:
139 argumentName = "info";
142 case ExceptionArgument.key:
143 argumentName = "key";
146 case ExceptionArgument.match:
147 argumentName = "match";
150 case ExceptionArgument.obj:
151 argumentName = "obj";
154 case ExceptionArgument.queue:
155 argumentName = "queue";
158 case ExceptionArgument.stack:
159 argumentName = "stack";
162 case ExceptionArgument.startIndex:
163 argumentName = "startIndex";
166 case ExceptionArgument.value:
167 argumentName = "value";
170 case ExceptionArgument.item:
171 argumentName = "item";
175 Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum.");
183 // This function will convert an ExceptionResource enum value to the resource string.
185 internal static string GetResourceName(ExceptionResource resource) {
186 string resourceName = null;
189 case ExceptionResource.Argument_ImplementIComparable:
190 resourceName = SR.Argument_ImplementIComparable;
193 case ExceptionResource.Argument_AddingDuplicate:
194 resourceName = SR.Argument_AddingDuplicate;
197 case ExceptionResource.ArgumentOutOfRange_Index:
198 resourceName = SR.ArgumentOutOfRange_Index;
201 case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum:
202 resourceName = SR.ArgumentOutOfRange_NeedNonNegNum;
205 case ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired:
206 resourceName = SR.ArgumentOutOfRange_NeedNonNegNumRequired;
209 case ExceptionResource.ArgumentOutOfRange_SmallCapacity:
210 resourceName = SR.ArgumentOutOfRange_SmallCapacity;
213 case ExceptionResource.Arg_ArrayPlusOffTooSmall:
214 resourceName = SR.Arg_ArrayPlusOffTooSmall;
217 case ExceptionResource.Arg_RankMultiDimNotSupported:
218 resourceName = SR.Arg_MultiRank;
221 case ExceptionResource.Arg_NonZeroLowerBound:
222 resourceName = SR.Arg_NonZeroLowerBound;
225 case ExceptionResource.Argument_InvalidArrayType:
226 resourceName = SR.Invalid_Array_Type;
229 case ExceptionResource.Argument_InvalidOffLen:
230 resourceName = SR.Argument_InvalidOffLen;
233 case ExceptionResource.InvalidOperation_CannotRemoveFromStackOrQueue:
234 resourceName = SR.InvalidOperation_CannotRemoveFromStackOrQueue;
237 case ExceptionResource.InvalidOperation_EmptyCollection:
238 resourceName = SR.InvalidOperation_EmptyCollection;
241 case ExceptionResource.InvalidOperation_EmptyQueue:
242 resourceName = SR.InvalidOperation_EmptyQueue;
245 case ExceptionResource.InvalidOperation_EnumOpCantHappen:
246 resourceName = SR.InvalidOperation_EnumOpCantHappen;
249 case ExceptionResource.InvalidOperation_EnumFailedVersion:
250 resourceName = SR.InvalidOperation_EnumFailedVersion;
253 case ExceptionResource.InvalidOperation_EmptyStack:
254 resourceName = SR.InvalidOperation_EmptyStack;
257 case ExceptionResource.InvalidOperation_EnumNotStarted:
258 resourceName = SR.InvalidOperation_EnumNotStarted;
261 case ExceptionResource.InvalidOperation_EnumEnded:
262 resourceName = SR.InvalidOperation_EnumEnded;
265 case ExceptionResource.NotSupported_KeyCollectionSet:
266 resourceName = SR.NotSupported_KeyCollectionSet;
269 case ExceptionResource.NotSupported_SortedListNestedWrite:
270 resourceName = SR.NotSupported_SortedListNestedWrite;
274 case ExceptionResource.Serialization_InvalidOnDeser:
275 resourceName = SR.Serialization_InvalidOnDeser;
278 case ExceptionResource.Serialization_MissingValues:
279 resourceName = SR.Serialization_MissingValues;
282 case ExceptionResource.Serialization_MismatchedCount:
283 resourceName = SR.Serialization_MismatchedCount;
287 case ExceptionResource.NotSupported_ValueCollectionSet:
288 resourceName = SR.NotSupported_ValueCollectionSet;
292 Debug.Assert(false, "The enum value is not defined, please checked ExceptionArgumentName Enum.");
302 // The convention for this enum is using the argument name as the enum name
304 internal enum ExceptionArgument {
325 // The convention for this enum is using the resource name as the enum name
327 internal enum ExceptionResource {
328 Argument_ImplementIComparable,
329 ArgumentOutOfRange_NeedNonNegNum,
330 ArgumentOutOfRange_NeedNonNegNumRequired,
331 Arg_ArrayPlusOffTooSmall,
332 Argument_AddingDuplicate,
333 Serialization_InvalidOnDeser,
334 Serialization_MismatchedCount,
335 Serialization_MissingValues,
336 Arg_RankMultiDimNotSupported,
337 Arg_NonZeroLowerBound,
338 Argument_InvalidArrayType,
339 NotSupported_KeyCollectionSet,
340 ArgumentOutOfRange_SmallCapacity,
341 ArgumentOutOfRange_Index,
342 Argument_InvalidOffLen,
343 NotSupported_ReadOnlyCollection,
344 InvalidOperation_CannotRemoveFromStackOrQueue,
345 InvalidOperation_EmptyCollection,
346 InvalidOperation_EmptyQueue,
347 InvalidOperation_EnumOpCantHappen,
348 InvalidOperation_EnumFailedVersion,
349 InvalidOperation_EmptyStack,
350 InvalidOperation_EnumNotStarted,
351 InvalidOperation_EnumEnded,
352 NotSupported_SortedListNestedWrite,
353 NotSupported_ValueCollectionSet,