| File: System\ThrowHelper.cs | Web Access |
| Project: src\runtime\src\libraries\System.Memory\src\System.Memory.csproj (System.Memory) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace System { // // This pattern of easily inlinable "void Throw" routines that stack on top of NoInlining factory methods // is a compromise between older JITs and newer JITs (RyuJIT in .NET Core 1.1.0+ and .NET Framework in 4.6.3+). // This package is explicitly targeted at older JITs as newer runtimes expect to implement Span intrinsically for // best performance. // // The aim of this pattern is three-fold // 1. Extracting the throw makes the method preforming the throw in a conditional branch smaller and more inlinable // 2. Extracting the throw from generic method to non-generic method reduces the repeated codegen size for value types // 3a. Newer JITs will not inline the methods that only throw and also recognise them, move the call to cold section // and not add stack prep and unwind before calling https://github.com/dotnet/coreclr/pull/6103 // 3b. Older JITs will inline the throw itself and move to cold section; but not inline the non-inlinable exception // factory methods - still maintaining advantages 1 & 2 // internal static class ThrowHelper { [DoesNotReturn] internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw CreateArgumentNullException(argument); } [MethodImpl(MethodImplOptions.NoInlining)] private static ArgumentNullException CreateArgumentNullException(ExceptionArgument argument) { return new ArgumentNullException(argument.ToString()); } [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) { throw CreateArgumentOutOfRangeException(argument); } [MethodImpl(MethodImplOptions.NoInlining)] private static ArgumentOutOfRangeException CreateArgumentOutOfRangeException(ExceptionArgument argument) { return new ArgumentOutOfRangeException(argument.ToString()); } [DoesNotReturn] internal static void ThrowInvalidOperationException() { throw CreateInvalidOperationException(); } [MethodImpl(MethodImplOptions.NoInlining)] private static InvalidOperationException CreateInvalidOperationException() { return new InvalidOperationException(); } [DoesNotReturn] internal static void ThrowInvalidOperationException_EndPositionNotReached() { throw CreateInvalidOperationException_EndPositionNotReached(); } [MethodImpl(MethodImplOptions.NoInlining)] private static InvalidOperationException CreateInvalidOperationException_EndPositionNotReached() { return new InvalidOperationException(SR.EndPositionNotReached); } [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException_PositionOutOfRange() { throw CreateArgumentOutOfRangeException_PositionOutOfRange(); } [MethodImpl(MethodImplOptions.NoInlining)] private static ArgumentOutOfRangeException CreateArgumentOutOfRangeException_PositionOutOfRange() { return new ArgumentOutOfRangeException("position"); } [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException_OffsetOutOfRange() { throw CreateArgumentOutOfRangeException_OffsetOutOfRange(); } [MethodImpl(MethodImplOptions.NoInlining)] private static ArgumentOutOfRangeException CreateArgumentOutOfRangeException_OffsetOutOfRange() { return new ArgumentOutOfRangeException(nameof(ExceptionArgument.offset)); } // // ReadOnlySequence .ctor validation Throws coalesced to enable inlining of the .ctor // [DoesNotReturn] public static void ThrowArgumentValidationException<T>(ReadOnlySequenceSegment<T>? startSegment, int startIndex, ReadOnlySequenceSegment<T>? endSegment) => throw CreateArgumentValidationException(startSegment, startIndex, endSegment); private static Exception CreateArgumentValidationException<T>(ReadOnlySequenceSegment<T>? startSegment, int startIndex, ReadOnlySequenceSegment<T>? endSegment) { if (startSegment == null) return CreateArgumentNullException(ExceptionArgument.startSegment); else if (endSegment == null) return CreateArgumentNullException(ExceptionArgument.endSegment); else if (startSegment != endSegment && startSegment.RunningIndex > endSegment.RunningIndex) return CreateArgumentOutOfRangeException(ExceptionArgument.endSegment); else if ((uint)startSegment.Memory.Length < (uint)startIndex) return CreateArgumentOutOfRangeException(ExceptionArgument.startIndex); else return CreateArgumentOutOfRangeException(ExceptionArgument.endIndex); } [DoesNotReturn] public static void ThrowArgumentValidationException(Array? array, int start) => throw CreateArgumentValidationException(array, start); private static Exception CreateArgumentValidationException(Array? array, int start) { if (array == null) return CreateArgumentNullException(ExceptionArgument.array); else if ((uint)start > (uint)array.Length) return CreateArgumentOutOfRangeException(ExceptionArgument.start); else return CreateArgumentOutOfRangeException(ExceptionArgument.length); } // // ReadOnlySequence Slice validation Throws coalesced to enable inlining of the Slice // [DoesNotReturn] public static void ThrowStartOrEndArgumentValidationException(long start) => throw CreateStartOrEndArgumentValidationException(start); private static ArgumentOutOfRangeException CreateStartOrEndArgumentValidationException(long start) { if (start < 0) return CreateArgumentOutOfRangeException(ExceptionArgument.start); return CreateArgumentOutOfRangeException(ExceptionArgument.length); } } // // The convention for this enum is using the argument name as the enum name // internal enum ExceptionArgument { length, start, minimumBufferSize, elementIndex, comparable, comparer, destination, offset, startSegment, endSegment, startIndex, endIndex, array, culture, manager, count, writer, } }