From be0808e864ec075866bf4db4278c40201640e4cb Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 12 Aug 2025 12:18:51 +0200 Subject: [PATCH] Address some of the AOT compiler errors in .NET 9 (#8649) (cherry picked from commit aa7618062196eb8d9ed86d406b5a3bbde20de106) --- .../Elastic.Clients.Elasticsearch.csproj | 2 +- .../Core/Extensions/ExpressionExtensions.cs | 4 ++++ .../Core/Infer/DynamicPropertyAccessor.cs | 2 ++ .../Infer/Field/FieldExpressionVisitor.cs | 4 ++++ .../_Shared/Core/Infer/Id/IdConverter.cs | 2 ++ .../_Shared/Core/Infer/Id/IdResolver.cs | 12 +++++----- .../_Shared/Core/Infer/Inferrer.cs | 9 +++---- .../JoinFieldRouting/RoutingConverter.cs | 2 ++ .../_Shared/Core/Infer/RoutingResolver.cs | 14 +++++++---- .../DefaultRequestResponseSerializer.cs | 5 ++-- .../Serialization/DefaultSourceSerializer.cs | 9 +++---- src/Playground/Playground.csproj | 2 +- src/Playground/Program.cs | 24 +++++++++++++++++++ 13 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj index bb9197f0bc9..13992fe296f 100644 --- a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj +++ b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj @@ -14,7 +14,7 @@ true true - netstandard2.0;net462;netstandard2.1;net8.0 + netstandard2.0;net462;netstandard2.1;net8.0;net9.0 true true annotations diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Extensions/ExpressionExtensions.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Extensions/ExpressionExtensions.cs index c95b207f3c8..408104fc704 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Extensions/ExpressionExtensions.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Extensions/ExpressionExtensions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; @@ -87,7 +88,10 @@ private class SuffixExpressionVisitor : ExpressionVisitor public SuffixExpressionVisitor(string suffix) => _suffix = suffix; + [RequiresDynamicCode("Use of Expression.Call requires dynamic code generation.")] +#pragma warning disable IL3051 public override Expression Visit(Expression node) => Expression.Call( +#pragma warning restore IL3051 typeof(SuffixExtensions), nameof(SuffixExtensions.Suffix), null, diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/DynamicPropertyAccessor.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/DynamicPropertyAccessor.cs index 74e8aaf3bea..02aeeb6e846 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/DynamicPropertyAccessor.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/DynamicPropertyAccessor.cs @@ -51,8 +51,10 @@ internal static class DynamicPropertyAccessor // Build compiled getter delegate. #pragma warning disable IL3050 +#pragma warning disable IL2060 var getterDelegateFactory = MakeDelegateMethodInfo.MakeGenericMethod(type, getterMethod.ReturnType); #pragma warning restore IL3050 +#pragma warning restore IL2060 var genericGetterDelegate = (Func)getterDelegateFactory.Invoke(null, [getterMethod])!; return instance => retrieverFunc(genericGetterDelegate, instance); diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Field/FieldExpressionVisitor.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Field/FieldExpressionVisitor.cs index 237ec3a2a80..65c04f68912 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Field/FieldExpressionVisitor.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Field/FieldExpressionVisitor.cs @@ -10,6 +10,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace Elastic.Clients.Elasticsearch; @@ -61,6 +62,8 @@ protected override Expression VisitMember(MemberExpression expression) return base.VisitMember(expression); } + [RequiresDynamicCode("Use of Expression.Lambda")] + [UnconditionalSuppressMessage("Trimming", "IL3051", Justification = "Can only annotate our implementation")] protected override Expression VisitMethodCall(MethodCallExpression methodCall) { if (methodCall.Method.Name == nameof(SuffixExtensions.Suffix) && methodCall.Arguments.Any()) @@ -97,6 +100,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return base.VisitMethodCall(methodCall); } + [RequiresDynamicCode("Use of Expression.Lambda")] private static void VisitConstantOrVariable(MethodCallExpression methodCall, Stack stack) { var lastArg = methodCall.Arguments.Last(); diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs index 4e4781092d8..a6728cab761 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch.Serialization; @@ -27,6 +28,7 @@ public override void WriteAsPropertyName(Utf8JsonWriter writer, Id value, JsonSe ? new Id(reader.GetInt64()) : new Id(reader.GetString()); + [UnconditionalSuppressMessage("AOT", "IL2072:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] public override void Write(Utf8JsonWriter writer, Id value, JsonSerializerOptions options) { if (value is null) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdResolver.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdResolver.cs index 59ad5ebc601..fc36e67fbff 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdResolver.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdResolver.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; namespace Elastic.Clients.Elasticsearch; @@ -16,12 +17,11 @@ public class IdResolver private readonly IElasticsearchClientSettings _settings; private readonly ConcurrentDictionary?> _localDelegateCache = new(); - public IdResolver(IElasticsearchClientSettings settings) - { - _settings = settings; - } + public IdResolver(IElasticsearchClientSettings settings) => _settings = settings; - public string? Resolve<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(T instance) + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] + [UnconditionalSuppressMessage("AOT", "IL2072:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] + public string? Resolve<[DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] T>(T instance) { if (_settings.DefaultDisableIdInference || (instance is null)) { @@ -31,7 +31,7 @@ public IdResolver(IElasticsearchClientSettings settings) return Resolve(instance.GetType(), instance); } - public string? Resolve([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance) + public string? Resolve([DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] Type type, object instance) { if (type is null) { diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Inferrer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Inferrer.cs index 83ff425e4f0..886ae41e0c9 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Inferrer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Inferrer.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using Elastic.Transport; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; namespace Elastic.Clients.Elasticsearch; @@ -40,15 +41,15 @@ public Inferrer(IElasticsearchClientSettings elasticsearchClientSettings) public string IndexName(IndexName index) => IndexNameResolver.Resolve(index); - public string? Id<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(T instance) => IdResolver.Resolve(instance); + public string? Id<[DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] T>(T instance) => IdResolver.Resolve(instance); - public string? Id([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance) => IdResolver.Resolve(type, instance); + public string? Id([DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] Type type, object instance) => IdResolver.Resolve(type, instance); public string RelationName() => RelationNameResolver.Resolve(); public string RelationName(RelationName type) => RelationNameResolver.Resolve(type); - public string? Routing<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(T document) => RoutingResolver.Resolve(document); + public string? Routing<[DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] T>(T document) => RoutingResolver.Resolve(document); - public string? Routing([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance) => RoutingResolver.Resolve(type, instance); + public string? Routing([DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] Type type, object instance) => RoutingResolver.Resolve(type, instance); } diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs index 1691211f273..f7b4d0c6efa 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/JoinFieldRouting/RoutingConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using Elastic.Clients.Elasticsearch.Serialization; @@ -18,6 +19,7 @@ internal sealed class RoutingConverter : JsonConverter ? new Routing(reader.GetInt64()) : new Routing(reader.GetString()); + [UnconditionalSuppressMessage("AOT", "IL2072:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] public override void Write(Utf8JsonWriter writer, Routing value, JsonSerializerOptions options) { if (value is null) diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/RoutingResolver.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/RoutingResolver.cs index b950e5c082e..3f2ea1d8c47 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/RoutingResolver.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/RoutingResolver.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Linq; using System.Globalization; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; namespace Elastic.Clients.Elasticsearch; @@ -23,7 +24,9 @@ public RoutingResolver(IElasticsearchClientSettings settings, IdResolver idResol _settings = settings; } - public string? Resolve<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(T instance) + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] + [UnconditionalSuppressMessage("AOT", "IL2072:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] + public string? Resolve<[DynamicallyAccessedMembers(PublicProperties | None | NonPublicProperties)] T>(T instance) { if (instance is null) { @@ -33,7 +36,8 @@ public RoutingResolver(IElasticsearchClientSettings settings, IdResolver idResol return Resolve(instance.GetType(), instance); } - public string? Resolve([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance) + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Call to object.GetType()")] + public string? Resolve([DynamicallyAccessedMembers(PublicProperties | None | NonPublicProperties)] Type type, object instance) { if (type is null) { @@ -59,7 +63,7 @@ public RoutingResolver(IElasticsearchClientSettings settings, IdResolver idResol } private bool TryGetConnectionSettingsRoute( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, + [DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] Type type, object instance, out string? route) { @@ -83,7 +87,7 @@ private bool TryGetConnectionSettingsRoute( } private static JoinField? GetJoinFieldFromObject( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, + [DynamicallyAccessedMembers(PublicProperties | NonPublicProperties)] Type type, object instance) { if (JoinFieldDelegateCache.TryGetValue(type, out var getterDelegate)) @@ -105,7 +109,7 @@ private bool TryGetConnectionSettingsRoute( return getterDelegate(instance); - static PropertyInfo? GetJoinFieldProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) + static PropertyInfo? GetJoinFieldProperty([DynamicallyAccessedMembers(PublicProperties)] Type type) { var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); try diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultRequestResponseSerializer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultRequestResponseSerializer.cs index 0f2680282a1..193931f45d7 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultRequestResponseSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultRequestResponseSerializer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; @@ -141,15 +142,15 @@ private static IReadOnlyCollection CreateDefaultBuiltInConverters new StringifiedDoubleConverter(), ]; + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] private static void MutateOptions(JsonSerializerOptions options) { -#pragma warning disable IL2026, IL3050 options.TypeInfoResolver = JsonTypeInfoResolver.Combine( RequestResponseSerializerContext.Default, ElasticsearchTransportSerializerContext.Default, new DefaultJsonTypeInfoResolver() ); -#pragma warning restore IL2026, IL3050 options.MaxDepth = 512; options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs index bff5642ef93..0173c0f20ae 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Serialization/DefaultSourceSerializer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -79,23 +80,23 @@ public DefaultSourceSerializerOptionsProvider(IElasticsearchClientSettings setti /// /// Returns an array of the built-in s that are registered with the source serializer by default. /// + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] private static IReadOnlyCollection CreateDefaultBuiltInConverters(IElasticsearchClientSettings settings) => [ // For context aware JsonConverter/JsonConverterFactory implementations. new ContextProvider(settings), -#pragma warning disable IL3050 new JsonStringEnumConverter(), -#pragma warning restore IL3050 new DoubleWithFractionalPortionConverter(), new SingleWithFractionalPortionConverter() ]; + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute'", Justification = "Always using explicit TypeInfoResolver")] private static void MutateOptions(JsonSerializerOptions options, IJsonTypeInfoResolver? typeInfoResolver, Action? configureOptions) { -#pragma warning disable IL2026, IL3050 options.TypeInfoResolver = typeInfoResolver ?? new DefaultJsonTypeInfoResolver(); -#pragma warning restore IL2026, IL3050 options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; diff --git a/src/Playground/Playground.csproj b/src/Playground/Playground.csproj index 5e1fb0a86ed..1d27a40460d 100644 --- a/src/Playground/Playground.csproj +++ b/src/Playground/Playground.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/src/Playground/Program.cs b/src/Playground/Program.cs index ad854df1d61..5990a7f8ed2 100644 --- a/src/Playground/Program.cs +++ b/src/Playground/Program.cs @@ -2,8 +2,11 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using Elastic.Clients.Elasticsearch; using Elastic.Transport; +using Elastic.Transport.Extensions; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; using Playground; @@ -18,3 +21,24 @@ var client = new ElasticsearchClient(settings); +var person = new Person +{ + FirstName = "Steve", + LastName = "Jobs", + Age = 35, + IsDeleted = false, + Routing = "1234567890", + Id = 1, + Enum = DateTimeKind.Utc, +}; + +var id = client.Infer.Id(person); +var idByType = IdByType(person.GetType(), person); +Console.WriteLine(id); +Console.WriteLine(idByType); +// This still errors on AOT compilation +//Console.WriteLine(client.SourceSerializer.SerializeToString(person)); + +[UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Can only annotate our implementation")] +[UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "Can only annotate our implementation")] +string? IdByType(Type type, object instance) => client.Infer.Id(type, instance);