Skip to content

Slightly better support for NativeAOT and trimming #35211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/Http/Http.Extensions/src/RequestDelegateFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ public static partial class RequestDelegateFactory
private static readonly ParameterExpression BodyValueExpr = Expression.Parameter(typeof(object), "bodyValue");
private static readonly ParameterExpression WasParamCheckFailureExpr = Expression.Variable(typeof(bool), "wasParamCheckFailure");

private static readonly MemberExpression RequestServicesExpr = Expression.Property(HttpContextExpr, nameof(HttpContext.RequestServices));
private static readonly MemberExpression HttpRequestExpr = Expression.Property(HttpContextExpr, nameof(HttpContext.Request));
private static readonly MemberExpression HttpResponseExpr = Expression.Property(HttpContextExpr, nameof(HttpContext.Response));
private static readonly MemberExpression RequestAbortedExpr = Expression.Property(HttpContextExpr, nameof(HttpContext.RequestAborted));
private static readonly MemberExpression UserExpr = Expression.Property(HttpContextExpr, nameof(HttpContext.User));
private static readonly MemberExpression RouteValuesExpr = Expression.Property(HttpRequestExpr, nameof(HttpRequest.RouteValues));
private static readonly MemberExpression QueryExpr = Expression.Property(HttpRequestExpr, nameof(HttpRequest.Query));
private static readonly MemberExpression HeadersExpr = Expression.Property(HttpRequestExpr, nameof(HttpRequest.Headers));
private static readonly MemberExpression StatusCodeExpr = Expression.Property(HttpResponseExpr, nameof(HttpResponse.StatusCode));
private static readonly MemberExpression RequestServicesExpr = Expression.Property(HttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.RequestServices))!);
private static readonly MemberExpression HttpRequestExpr = Expression.Property(HttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.Request))!);
private static readonly MemberExpression HttpResponseExpr = Expression.Property(HttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.Response))!);
private static readonly MemberExpression RequestAbortedExpr = Expression.Property(HttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.RequestAborted))!);
private static readonly MemberExpression UserExpr = Expression.Property(HttpContextExpr, typeof(HttpContext).GetProperty(nameof(HttpContext.User))!);
private static readonly MemberExpression RouteValuesExpr = Expression.Property(HttpRequestExpr, typeof(HttpRequest).GetProperty(nameof(HttpRequest.RouteValues))!);
private static readonly MemberExpression QueryExpr = Expression.Property(HttpRequestExpr, typeof(HttpRequest).GetProperty(nameof(HttpRequest.Query))!);
private static readonly MemberExpression HeadersExpr = Expression.Property(HttpRequestExpr, typeof(HttpRequest).GetProperty(nameof(HttpRequest.Headers))!);
private static readonly MemberExpression StatusCodeExpr = Expression.Property(HttpResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
private static readonly MemberExpression CompletedTaskExpr = Expression.Property(null, (PropertyInfo)GetMemberInfo<Func<Task>>(() => Task.CompletedTask));

private static readonly BinaryExpression TempSourceStringNotNullExpr = Expression.NotEqual(TryParseMethodCache.TempSourceStringExpr, Expression.Constant(null));
Expand Down
256 changes: 146 additions & 110 deletions src/Shared/TryParseMethodCache.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;

#nullable enable

Expand All @@ -24,7 +23,9 @@ internal sealed class TryParseMethodCache

internal readonly ParameterExpression TempSourceStringExpr = Expression.Variable(typeof(string), "tempSourceString");

public TryParseMethodCache() : this(preferNonGenericEnumParseOverload: false)
// If IsDynamicCodeSupported is false, we can't use the static Enum.TryParse<T> since there's no easy way for
// this code to generate the specific instantiation for any enums used
public TryParseMethodCache() : this(preferNonGenericEnumParseOverload: !RuntimeFeature.IsDynamicCodeSupported)
{
}

Expand Down Expand Up @@ -121,146 +122,181 @@ public bool HasTryParseMethod(ParameterInfo parameter)

private static MethodInfo GetEnumTryParseMethod(bool preferNonGenericEnumParseOverload)
{
var staticEnumMethods = typeof(Enum).GetMethods(BindingFlags.Public | BindingFlags.Static);
MethodInfo? methodInfo = null;

// With NativeAOT, if there's no static usage of Enum.TryParse<T>, it will be removed
// we fallback to the non-generic version if that is the case
MethodInfo? genericCandidate = null;
MethodInfo? nonGenericCandidate = null;

foreach (var method in staticEnumMethods)
if (preferNonGenericEnumParseOverload)
{
if (method.Name != nameof(Enum.TryParse) || method.ReturnType != typeof(bool))
{
continue;
}

var tryParseParameters = method.GetParameters();

// Enum.TryParse<T>(string, out object)
if (method.IsGenericMethod &&
tryParseParameters.Length == 2 &&
tryParseParameters[0].ParameterType == typeof(string) &&
tryParseParameters[1].IsOut)
{
genericCandidate = method;
}

// Enum.TryParse(type, string, out object)
if (!method.IsGenericMethod &&
tryParseParameters.Length == 3 &&
tryParseParameters[0].ParameterType == typeof(Type) &&
tryParseParameters[1].ParameterType == typeof(string) &&
tryParseParameters[2].IsOut)
{
nonGenericCandidate = method;
}
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(Type), typeof(string), typeof(object).MakeByRefType() });
Comment on lines +129 to +132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(Type), typeof(string), typeof(object).MakeByRefType() });
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(Type), typeof(string), typeof(object).MakeByRefType() });

}

if (genericCandidate is null && nonGenericCandidate is null)
else
{
Debug.Fail("No suitable System.Enum.TryParse method found.");
throw new MissingMethodException("No suitable System.Enum.TryParse method found.");
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
genericParameterCount: 1,
new[] { typeof(string), Type.MakeGenericMethodParameter(0).MakeByRefType() });
Comment on lines +136 to +139
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
genericParameterCount: 1,
new[] { typeof(string), Type.MakeGenericMethodParameter(0).MakeByRefType() });
methodInfo = typeof(Enum).GetMethod(
nameof(Enum.TryParse),
genericParameterCount: 1,
new[] { typeof(string), Type.MakeGenericMethodParameter(0).MakeByRefType() });

}

if (preferNonGenericEnumParseOverload)
if (methodInfo is null)
{
return nonGenericCandidate!;
Debug.Fail("No suitable System.Enum.TryParse method found.");
throw new MissingMethodException("No suitable System.Enum.TryParse method found.");
}

return genericCandidate ?? nonGenericCandidate!;
return methodInfo!;
}

private static bool TryGetDateTimeTryParseMethod(Type type, [NotNullWhen(true)] out MethodInfo? methodInfo)
{
methodInfo = null;
if (type != typeof(DateTime) && type != typeof(DateOnly) &&
type != typeof(DateTimeOffset) && type != typeof(TimeOnly))

if (type == typeof(DateTime))
{
return false;
methodInfo = typeof(DateTime).GetMethod(
nameof(DateTime.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateTime).MakeByRefType() });
}
else if (type == typeof(DateTimeOffset))
{
methodInfo = typeof(DateTimeOffset).GetMethod(
nameof(DateTimeOffset.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateTimeOffset).MakeByRefType() });
}
else if (type == typeof(DateOnly))
{
methodInfo = typeof(DateOnly).GetMethod(
nameof(DateOnly.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateOnly).MakeByRefType() });
}
else if (type == typeof(TimeOnly))
{
methodInfo = typeof(TimeOnly).GetMethod(
nameof(TimeOnly.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(TimeOnly).MakeByRefType() });
}

var staticTryParseDateMethod = type.GetMethod(
"TryParse",
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), type.MakeByRefType() });

methodInfo = staticTryParseDateMethod;

return methodInfo != null;
}

private static bool TryGetNumberStylesTryGetMethod(Type type, [NotNullWhen(true)] out MethodInfo? method, [NotNullWhen(true)] out NumberStyles? numberStyles)
{
method = null;
numberStyles = null;
numberStyles = NumberStyles.Integer;

if (!UseTryParseWithNumberStyleOption(type))
if (type == typeof(long))
{
return false;
method = typeof(long).GetMethod(
nameof(long.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(long).MakeByRefType() });
Comment on lines +194 to +197
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
method = typeof(long).GetMethod(
nameof(long.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(long).MakeByRefType() });
method = typeof(long).GetMethod(
nameof(long.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(long).MakeByRefType() });

}

var staticMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "TryParse" && m.ReturnType == typeof(bool))
.OrderByDescending(m => m.GetParameters().Length);

var numberStylesToUse = NumberStyles.Integer;
var methodToUse = default(MethodInfo);

foreach (var methodInfo in staticMethods)
else if (type == typeof(ulong))
{
var tryParseParameters = methodInfo.GetParameters();

if (tryParseParameters.Length == 4 &&
tryParseParameters[0].ParameterType == typeof(string) &&
tryParseParameters[1].ParameterType == typeof(NumberStyles) &&
tryParseParameters[2].ParameterType == typeof(IFormatProvider) &&
tryParseParameters[3].IsOut &&
tryParseParameters[3].ParameterType == type.MakeByRefType())
{
if (type == typeof(int) || type == typeof(short) || type == typeof(IntPtr) ||
type == typeof(long) || type == typeof(byte) || type == typeof(sbyte) ||
type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) ||
type == typeof(BigInteger))
{
numberStylesToUse = NumberStyles.Integer;
}
method = typeof(ulong).GetMethod(
nameof(ulong.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(ulong).MakeByRefType() });
}
else if (type == typeof(int))
{
method = typeof(int).GetMethod(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting for all of these

nameof(int.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(int).MakeByRefType() });
}
else if (type == typeof(uint))
{
method = typeof(uint).GetMethod(
nameof(uint.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(uint).MakeByRefType() });
}
else if (type == typeof(short))
{
method = typeof(short).GetMethod(
nameof(short.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(short).MakeByRefType() });
}
else if (type == typeof(ushort))
{
method = typeof(ushort).GetMethod(
nameof(ushort.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(ushort).MakeByRefType() });
}
else if (type == typeof(byte))
{
method = typeof(byte).GetMethod(
nameof(byte.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(byte).MakeByRefType() });
}
else if (type == typeof(sbyte))
{
method = typeof(sbyte).GetMethod(
nameof(sbyte.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(sbyte).MakeByRefType() });
}
else if (type == typeof(double))
{
method = typeof(double).GetMethod(
nameof(double.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(double).MakeByRefType() });

if (type == typeof(double) || type == typeof(float) || type == typeof(Half))
{
numberStylesToUse = NumberStyles.AllowThousands | NumberStyles.Float;
}
numberStyles = NumberStyles.AllowThousands | NumberStyles.Float;
}
else if (type == typeof(float))
{
method = typeof(float).GetMethod(
nameof(float.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(float).MakeByRefType() });

if (type == typeof(decimal))
{
numberStylesToUse = NumberStyles.Number;
}
numberStyles = NumberStyles.AllowThousands | NumberStyles.Float;
}
else if (type == typeof(Half))
{
method = typeof(Half).GetMethod(
nameof(Half.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(Half).MakeByRefType() });

methodToUse = methodInfo!;
break;
}
numberStyles = NumberStyles.AllowThousands | NumberStyles.Float;
}
else if (type == typeof(decimal))
{
method = typeof(decimal).GetMethod(
nameof(decimal.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(decimal).MakeByRefType() });

numberStyles = numberStylesToUse!;
method = methodToUse!;
numberStyles = NumberStyles.Number;
}
else if (type == typeof(IntPtr))
{
method = typeof(IntPtr).GetMethod(
nameof(IntPtr.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(IntPtr).MakeByRefType() });
}
else if (type == typeof(BigInteger))
{
method = typeof(BigInteger).GetMethod(
nameof(BigInteger.TryParse),
BindingFlags.Public | BindingFlags.Static,
new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(BigInteger).MakeByRefType() });
}

return true;
return method != null;
}

internal static bool UseTryParseWithNumberStyleOption(Type type)
=> type == typeof(int) ||
type == typeof(double) ||
type == typeof(decimal) ||
type == typeof(float) ||
type == typeof(Half) ||
type == typeof(short) ||
type == typeof(long) ||
type == typeof(IntPtr) ||
type == typeof(byte) ||
type == typeof(sbyte) ||
type == typeof(ushort) ||
type == typeof(uint) ||
type == typeof(ulong) ||
type == typeof(BigInteger);
}
}