-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Respecting EnumMemberAttribute in AOT'd applications #97737
Comments
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsI'd like to NativeAOT compile a library that respects the EnumMemberAttribute. This attribute can be applied to enum fields to rename what the field name is used when serializing values of an enum. The issue is, I can't statically know all the enum types that will be passed into my code. So I need to write code like this: private static object ReplaceEnumValueByStringRepresentation(object source)
{
if(source is Enum enumValue && GetEnumName(enumValue) is string enumValueName)
{
return enumValueName;
}
return source;
}
private static string? GetEnumName<T>(T value) where T : Enum
{
var type = value.GetType();
if(Enum.GetName(type, value) is not { } name)
throw new ArgumentException($"Invalid Enum value {value} for enum of type {type}");
if(type.GetField(name)?.GetCustomAttribute<EnumMemberAttribute>() is { } attribute)
return attribute.Value;
return name.ToFirstCharacterLowerCase(); But I am getting a warning on In talking with @MichalStrehovsky and @agocke, one recommendation is that since enum fields cannot be trimmed, we can suppress this warning. We should document this so people can justify suppressing this warning. Additionally, it would be great if the analysis tooling understood that since
|
I think that instead of |
Initially I agreed, until I saw the code in the top method: private static object ReplaceEnumValueByStringRepresentation(object source)
{
if(source is Enum enumValue && GetEnumName(enumValue) is string enumValueName) here is a call to
|
Fixes dotnet#97737. Trimming ensures we keep all the public fields on enums. `typeof(T).GetFields()` is safe when the `T` is constrained to be `System.Enum` or a derived type.
Fixes #97737. Trimming ensures we keep all the public fields on enums. `typeof(T).GetFields()` is safe when the `T` is constrained to be `System.Enum` or a derived type.
…#100814) Fixes dotnet#97737. Trimming ensures we keep all the public fields on enums. `typeof(T).GetFields()` is safe when the `T` is constrained to be `System.Enum` or a derived type.
…#100814) Fixes dotnet#97737. Trimming ensures we keep all the public fields on enums. `typeof(T).GetFields()` is safe when the `T` is constrained to be `System.Enum` or a derived type.
Reopening, we also need to handle the |
@sbomer, I reopened this because the original use case was actually calling out for a variation of what was fixed. The same variation was also hit in microsoft/OpenAPI.NET#1717 (comment). I think we'd want to fix this, but the fix doesn't look exactly trivial on the ILLinker side. The native AOT side is probably just: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
index 74fe05d9bd8..fcb9695a485 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs
@@ -15,6 +15,7 @@
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
using WellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType;
+using System.Diagnostics.CodeAnalysis;
#nullable enable
@@ -392,8 +393,29 @@ private partial bool TryHandleIntrinsic (
TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType?.Type;
if (staticType is null || (!staticType.IsDefType && !staticType.IsArray))
{
- // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations"
- AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj));
+ DynamicallyAccessedMemberTypes annotation = default;
+ if (staticType is { IsSignatureVariable: true })
+ {
+ var genericParam = (GenericParameterDesc)staticType.InstantiateSignature(_callingMethod.OwningType.Instantiation, _callingMethod.Instantiation);
+ foreach (TypeDesc constraint in genericParam.TypeConstraints)
+ {
+ if (constraint.IsWellKnownType(Internal.TypeSystem.WellKnownType.Enum))
+ {
+ annotation = DynamicallyAccessedMemberTypes.PublicFields;
+ break;
+ }
+ }
+ }
+
+ if (annotation != default)
+ {
+ AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj, annotation));
+ }
+ else
+ {
+ // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations"
+ AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj));
+ }
}
else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate"))
{ However, on ILLinker side this is running into the following big comment: runtime/src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs Lines 108 to 121 in 66ae898
I.e. the |
It looks like we could change |
I'd like to NativeAOT compile a library that respects the EnumMemberAttribute. This attribute can be applied to enum fields to rename what the field name is used when serializing values of an enum.
The issue is, I can't statically know all the enum types that will be passed into my code. So I need to write code like this:
https://github.com/microsoft/kiota-abstractions-dotnet/blob/02afdff08829667ad7411e87b9dfa797809243a0/src/RequestInformation.cs#L187-L210
But I am getting a warning on
value.GetType()
and using it to calltype.GetField(name)
.In talking with @MichalStrehovsky and @agocke, one recommendation is that since enum fields cannot be trimmed, we can suppress this warning.
We should document this so people can justify suppressing this warning.
Additionally, it would be great if the analysis tooling understood that since
T : Enum
, and we are callingGetField
on a System.Type that is guaranteed to be an enum, we shouldn't warn.The text was updated successfully, but these errors were encountered: