-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -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) | ||||||||||||||||||
{ | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -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() }); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
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( | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||||||||||||||||||
} | ||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.