Skip to content

Commit

Permalink
Fix a linker warning with the JsonSerializerOptionsUpdateHandler (#10…
Browse files Browse the repository at this point in the history
…0362)

* * Fixes a linker warning with JsonSerializerOptionsUpdateHandler.
* Makes a few cleanups in the MemberAccessor clases.
* Ensures that the MemberAccessor being used is a singleton.

* Fix a number of trimmability warnings.
  • Loading branch information
eiriktsarpalis authored Mar 28, 2024
1 parent a5b9a6a commit d80a09f
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 75 deletions.
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

0 comments on commit d80a09f

Please sign in to comment.