Skip to content

Fix a linker warning with the JsonSerializerOptionsUpdateHandler #100362

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

Expand All @@ -25,11 +23,7 @@ public static void ClearCache(Type[]? types)
options.Key.ClearCaches();
}

if (RuntimeFeature.IsDynamicCodeSupported)
{
// Flush the dynamic method cache
ReflectionEmitCachingMemberAccessor.Clear();
}
DefaultJsonTypeInfoResolver.ClearMemberAccessorCaches();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,38 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json.Reflection;
using System.Threading;

namespace System.Text.Json.Serialization.Metadata
{
public partial class DefaultJsonTypeInfoResolver
{
internal static MemberAccessor MemberAccessor
{
[RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
get
{
return s_memberAccessor ??=
return s_memberAccessor ?? Initialize();
static MemberAccessor Initialize()
{
MemberAccessor value =
#if NETCOREAPP
// if dynamic code isn't supported, fallback to reflection
RuntimeFeature.IsDynamicCodeSupported ?
new ReflectionEmitCachingMemberAccessor() :
new ReflectionMemberAccessor();
// if dynamic code isn't supported, fallback to reflection
RuntimeFeature.IsDynamicCodeSupported ?
new ReflectionEmitCachingMemberAccessor() :
new ReflectionMemberAccessor();
#elif NETFRAMEWORK
new ReflectionEmitCachingMemberAccessor();
new ReflectionEmitCachingMemberAccessor();
#else
new ReflectionMemberAccessor();
new ReflectionMemberAccessor();
#endif
return Interlocked.CompareExchange(ref s_memberAccessor, value, null) ?? value;
}
}
}

internal static void ClearMemberAccessorCaches() => s_memberAccessor?.Clear();
private static MemberAccessor? s_memberAccessor;

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,16 @@ namespace System.Text.Json.Serialization.Metadata
{
internal abstract class MemberAccessor
{
public abstract Func<object>? CreateParameterlessConstructor(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
ConstructorInfo? constructorInfo);
public abstract Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? constructorInfo);

public abstract Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor);

public abstract JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>?
CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor);
public abstract JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor);

public abstract Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>();

[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
[RequiresDynamicCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
public abstract Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>();

[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
[RequiresDynamicCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
public abstract Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>();

public abstract Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo);
Expand All @@ -35,5 +28,7 @@ public abstract JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T
public abstract Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo);

public abstract Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo);

public virtual void Clear() { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,70 @@

namespace System.Text.Json.Serialization.Metadata
{
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
internal sealed partial class ReflectionEmitCachingMemberAccessor : MemberAccessor
{
private static readonly ReflectionEmitMemberAccessor s_sourceAccessor = new();
private static readonly Cache<(string id, Type declaringType, MemberInfo? member)> s_cache =
new(slidingExpiration: TimeSpan.FromMilliseconds(1000), evictionInterval: TimeSpan.FromMilliseconds(200));
private readonly ReflectionEmitMemberAccessor _sourceAccessor;
private readonly Cache<(string id, Type declaringType, MemberInfo? member)> _cache;

public static void Clear() => s_cache.Clear();
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
[RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
public ReflectionEmitCachingMemberAccessor()
{
_sourceAccessor = new ReflectionEmitMemberAccessor();
_cache = new(slidingExpiration: TimeSpan.FromMilliseconds(1000), evictionInterval: TimeSpan.FromMilliseconds(200));
}

public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>()
=> s_cache.GetOrAdd((nameof(CreateAddMethodDelegate), typeof(TCollection), null),
static (_) => s_sourceAccessor.CreateAddMethodDelegate<TCollection>());
public override void Clear() => _cache.Clear();

public override Func<object>? CreateParameterlessConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, ConstructorInfo? ctorInfo)
=> s_cache.GetOrAdd((nameof(CreateParameterlessConstructor), type, ctorInfo),
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2077:UnrecognizedReflectionPattern",
Justification = "Cannot apply DynamicallyAccessedMembersAttribute to tuple properties.")]
#pragma warning disable IL2077 // The suppression doesn't work for the trim analyzer: https://github.com/dotnet/roslyn/issues/59746
static (key) => s_sourceAccessor.CreateParameterlessConstructor(key.declaringType, (ConstructorInfo?)key.member));
#pragma warning restore IL2077
public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>() =>
_cache.GetOrAdd(
key: (nameof(CreateAddMethodDelegate), typeof(TCollection), null),
_ => _sourceAccessor.CreateAddMethodDelegate<TCollection>());

public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo)
=> s_cache.GetOrAdd((nameof(CreateFieldGetter), typeof(TProperty), fieldInfo), static key => s_sourceAccessor.CreateFieldGetter<TProperty>((FieldInfo)key.member!));
public override Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? ctorInfo) =>
_cache.GetOrAdd(
key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
valueFactory: key => _sourceAccessor.CreateParameterlessConstructor(key.declaringType, (ConstructorInfo?)key.member));

public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo)
=> s_cache.GetOrAdd((nameof(CreateFieldSetter), typeof(TProperty), fieldInfo), static key => s_sourceAccessor.CreateFieldSetter<TProperty>((FieldInfo)key.member!));
public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
_cache.GetOrAdd(
key: (nameof(CreateFieldGetter), typeof(TProperty), fieldInfo),
valueFactory: key => _sourceAccessor.CreateFieldGetter<TProperty>((FieldInfo)key.member!));

[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>()
=> s_cache.GetOrAdd((nameof(CreateImmutableDictionaryCreateRangeDelegate), typeof((TCollection, TKey, TValue)), null),
static (_) => s_sourceAccessor.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>());
public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo) =>
_cache.GetOrAdd(
key: (nameof(CreateFieldSetter), typeof(TProperty), fieldInfo),
valueFactory: key => _sourceAccessor.CreateFieldSetter<TProperty>((FieldInfo)key.member!));

[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
=> s_cache.GetOrAdd((nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null),
static (_) => s_sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>());
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>() =>
_cache.GetOrAdd(
key: (nameof(CreateImmutableDictionaryCreateRangeDelegate), typeof((TCollection, TKey, TValue)), null),
valueFactory: _ => _sourceAccessor.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>());

public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
=> s_cache.GetOrAdd((nameof(CreateParameterizedConstructor), typeof(T), constructor), static key => s_sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member!));
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>() =>
_cache.GetOrAdd(
key: (nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null),
valueFactory: _ => _sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>());

public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor)
=> s_cache.GetOrAdd((nameof(CreateParameterizedConstructor), typeof(T), constructor), static key => s_sourceAccessor.CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>((ConstructorInfo)key.member!));
public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
_cache.GetOrAdd(
key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member!));

public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo)
=> s_cache.GetOrAdd((nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo), static key => s_sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member!));
public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor) =>
_cache.GetOrAdd(
key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>((ConstructorInfo)key.member!));

public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo)
=> s_cache.GetOrAdd((nameof(CreatePropertySetter), typeof(TProperty), propertyInfo), static key => s_sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member!));
public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
_cache.GetOrAdd(
key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
valueFactory: key => _sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member!));

public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
_cache.GetOrAdd(
key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
valueFactory: key => _sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member!));
}
}
#endif
Loading