Skip to content

Commit

Permalink
Improve trimmer suppressions in async code (#52432)
Browse files Browse the repository at this point in the history
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
  • Loading branch information
vitek-karas and eerhardt committed May 11, 2021
1 parent 753b764 commit d4fc640
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,6 @@
using System.Threading;
using System.Threading.Tasks;

[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Target = "M:System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__12.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2077:UnrecognizedReflectionPattern",
Target = "M:System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__12.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Target = "M:System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Target = "M:System.Net.Http.Json.HttpClientJsonExtensions.<GetFromJsonAsyncCore>d__13`1.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]

namespace System.Net.Http.Json
{
/// <summary>
Expand Down Expand Up @@ -149,8 +132,17 @@ public static partial class HttpClientJsonExtensions
// Nullable forgiving reason:
// GetAsync will usually return Content as not-null.
// If Content happens to be null, the extension will throw.
return await response.Content!.ReadFromJsonAsync(type, options, cancellationToken).ConfigureAwait(false);
return await ReadFromJsonAsyncHelper(response.Content!, type, options, cancellationToken).ConfigureAwait(false);
}

// Workaround for https://github.com/mono/linker/issues/1416, extracting the offending call into a separate method
// which can be annotated with suppressions.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
static Task<object?> ReadFromJsonAsyncHelper(HttpContent content, Type type, JsonSerializerOptions? options, CancellationToken cancellationToken)
=> content.ReadFromJsonAsync(type, options, cancellationToken);
}

[RequiresUnreferencedCode(HttpContentJsonExtensions.SerializationUnreferencedCodeMessage)]
Expand All @@ -162,10 +154,22 @@ public static partial class HttpClientJsonExtensions
// Nullable forgiving reason:
// GetAsync will usually return Content as not-null.
// If Content happens to be null, the extension will throw.
return await response.Content!.ReadFromJsonAsync<T>(options, cancellationToken).ConfigureAwait(false);
return await ReadFromJsonAsyncHelper<T>(response.Content!, options, cancellationToken).ConfigureAwait(false);
}
}

// Workaround for https://github.com/mono/linker/issues/1416, extracting the offending call into a separate method
// which can be annotated with suppressions.
// Note that in this case it can't be a local function since that inherits a generic parameter from the parent method
// which causes a trimmer warning coming from compiler generated code, which is very hard to suppress.
// Avoid that by declaring it a normal method which fully defines its own generic parameters.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
private static Task<T?> ReadFromJsonAsyncHelper<T>(HttpContent content, JsonSerializerOptions? options, CancellationToken cancellationToken)
=> content.ReadFromJsonAsync<T>(options, cancellationToken);

private static async Task<object?> GetFromJsonAsyncCore(Task<HttpResponseMessage> taskResponse, Type type, JsonSerializerContext context, CancellationToken cancellationToken)
{
using (HttpResponseMessage response = await taskResponse.ConfigureAwait(false))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,6 @@
using System.Threading;
using System.Threading.Tasks;

[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Target = "M:System.Net.Http.Json.HttpContentJsonExtensions.<ReadFromJsonAsyncCore>d__3.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2077:UnrecognizedReflectionPattern",
Target = "M:System.Net.Http.Json.HttpContentJsonExtensions.<ReadFromJsonAsyncCore>d__3.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Target = "M:System.Net.Http.Json.HttpContentJsonExtensions.<ReadFromJsonAsyncCore>d__4`1.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Target = "M:System.Net.Http.Json.HttpContentJsonExtensions.<ReadFromJsonAsyncCore>d__4`1.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]

namespace System.Net.Http.Json
{
public static partial class HttpContentJsonExtensions
Expand Down Expand Up @@ -64,19 +47,33 @@ public static partial class HttpContentJsonExtensions
{
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
return await JsonSerializer.DeserializeAsync(contentStream, type, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
return await DeserializeAsyncHelper(contentStream, type, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
static ValueTask<object?> DeserializeAsyncHelper(Stream contentStream, Type returnType, JsonSerializerOptions? options, CancellationToken cancellationToken)
=> JsonSerializer.DeserializeAsync(contentStream, returnType, options, cancellationToken);
}

[RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)]
private static async Task<T?> ReadFromJsonAsyncCore<[DynamicallyAccessedMembers(JsonHelpers.DeserializationMemberTypes)] T>(HttpContent content, Encoding? sourceEncoding, JsonSerializerOptions? options, CancellationToken cancellationToken)
{
using (Stream contentStream = await GetContentStream(content, sourceEncoding, cancellationToken).ConfigureAwait(false))
{
return await JsonSerializer.DeserializeAsync<T>(contentStream, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
return await DeserializeAsyncHelper<T>(contentStream, options ?? JsonContent.s_defaultSerializerOptions, cancellationToken).ConfigureAwait(false);
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
private static ValueTask<TValue?> DeserializeAsyncHelper<TValue>(Stream contentStream, JsonSerializerOptions? options, CancellationToken cancellationToken)
=> JsonSerializer.DeserializeAsync<TValue>(contentStream, options, cancellationToken);

public static Task<object?> ReadFromJsonAsync(this HttpContent content, Type type, JsonSerializerContext context, CancellationToken cancellationToken = default)
{
if (content == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
using System.Threading;
using System.Threading.Tasks;

[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Target = "M:System.Net.Http.Json.JsonContent.<SerializeToStreamAsyncCore>d__13.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as UnconditionalSuppressMessage.")]

namespace System.Net.Http.Json
{
public sealed partial class JsonContent : HttpContent
Expand Down Expand Up @@ -83,14 +78,14 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
{
if (async)
{
await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await SerializeAsyncHelper(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
}
else
{
// Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly.
// ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574
using var writer = new Utf8JsonWriter(transcodingStream);
JsonSerializer.Serialize(writer, Value, ObjectType, _jsonSerializerOptions);
SerializeHelper(writer, Value, ObjectType, _jsonSerializerOptions);
}
}
finally
Expand All @@ -111,7 +106,7 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C

using (TranscodingWriteStream transcodingStream = new TranscodingWriteStream(targetStream, targetEncoding))
{
await JsonSerializer.SerializeAsync(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await SerializeAsyncHelper(transcodingStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
// The transcoding streams use Encoders and Decoders that have internal buffers. We need to flush these
// when there is no more data to be written. Stream.FlushAsync isn't suitable since it's
// acceptable to Flush a Stream (multiple times) prior to completion.
Expand All @@ -123,20 +118,32 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
{
if (async)
{
await JsonSerializer.SerializeAsync(targetStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
await SerializeAsyncHelper(targetStream, Value, ObjectType, _jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
}
else
{
#if NETCOREAPP
// Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly.
// ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574
using var writer = new Utf8JsonWriter(targetStream);
JsonSerializer.Serialize(writer, Value, ObjectType, _jsonSerializerOptions);
SerializeHelper(writer, Value, ObjectType, _jsonSerializerOptions);
#else
Debug.Fail("Synchronous serialization is only supported since .NET 5.0");
#endif
}
}

#if NETCOREAPP
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
static void SerializeHelper(Utf8JsonWriter writer, object? value, [DynamicallyAccessedMembers(JsonHelpers.SerializationMemberTypes)] Type inputType, JsonSerializerOptions? options)
=> JsonSerializer.Serialize(writer, value, inputType, options);
#endif

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]
static Task SerializeAsyncHelper(Stream utf8Json, object? value, [DynamicallyAccessedMembers(JsonHelpers.SerializationMemberTypes)] Type inputType, JsonSerializerOptions? options, CancellationToken cancellationToken)
=> JsonSerializer.SerializeAsync(utf8Json, value, inputType, options, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@
using System.Threading;
using System.Threading.Tasks;

[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern",
Target = "M:System.Text.Json.JsonSerializer.<<DeserializeAsyncEnumerable>g__CreateAsyncEnumerableDeserializer|27_0>d`1.MoveNext()",
Scope = "member",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. The outer method is marked as RequiresUnreferencedCode.")]

namespace System.Text.Json
{
public static partial class JsonSerializer
Expand Down Expand Up @@ -229,7 +224,7 @@ public static partial class JsonSerializer
var bufferState = new ReadAsyncBufferState(options.DefaultBufferSize);
// Hardcode the queue converter to avoid accidental use of custom converters
JsonConverter converter = QueueOfTConverter<Queue<TValue>, TValue>.Instance;
JsonTypeInfo jsonTypeInfo = CreateQueueJsonTypeInfo(converter, options);
JsonTypeInfo jsonTypeInfo = CreateQueueJsonTypeInfo<TValue>(converter, options);
ReadStack readStack = default;
readStack.Initialize(jsonTypeInfo, supportContinuation: true);
var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
Expand All @@ -255,13 +250,15 @@ public static partial class JsonSerializer
bufferState.Dispose();
}
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")]
static JsonTypeInfo CreateQueueJsonTypeInfo(JsonConverter queueConverter, JsonSerializerOptions queueOptions) =>
new JsonTypeInfo(typeof(Queue<TValue>), queueConverter, typeof(Queue<TValue>), queueOptions);
}

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")]
private static JsonTypeInfo CreateQueueJsonTypeInfo<TValue>(JsonConverter queueConverter, JsonSerializerOptions queueOptions) =>
new JsonTypeInfo(typeof(Queue<TValue>), queueConverter, typeof(Queue<TValue>), queueOptions);

internal static async ValueTask<TValue?> ReadAllAsync<TValue>(
Stream utf8Json,
Type inputType,
Expand Down

0 comments on commit d4fc640

Please sign in to comment.