ReflectionEmitCachingMemberAccessor
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
namespace System.Text.Json.Serialization.Metadata
{
[RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")]
internal sealed class ReflectionEmitCachingMemberAccessor : MemberAccessor
{
private sealed class Cache<TKey>
{
private sealed class CacheEntry
{
public readonly object Value;
public long LastUsedTicks;
public CacheEntry(object value)
{
Value = value;
}
}
private int _evictLock;
private long _lastEvictedTicks;
private readonly long _evictionIntervalTicks;
private readonly long _slidingExpirationTicks;
private readonly ConcurrentDictionary<TKey, CacheEntry> _cache = new ConcurrentDictionary<TKey, CacheEntry>();
public Cache(TimeSpan slidingExpiration, TimeSpan evictionInterval)
{
_slidingExpirationTicks = slidingExpiration.Ticks;
_evictionIntervalTicks = evictionInterval.Ticks;
_lastEvictedTicks = DateTime.UtcNow.Ticks;
}
public TValue GetOrAdd<TValue>(TKey key, Func<TKey, TValue> valueFactory) where TValue : class
{
CacheEntry orAdd = this._cache.GetOrAdd(key, (TKey key, Func<TKey, TValue> valueFactory) => new CacheEntry((object)valueFactory(key)), valueFactory);
long ticks = DateTime.UtcNow.Ticks;
Volatile.Write(ref orAdd.LastUsedTicks, ticks);
if (ticks - Volatile.Read(ref this._lastEvictedTicks) >= this._evictionIntervalTicks && Interlocked.CompareExchange(ref this._evictLock, 1, 0) == 0) {
if (ticks - this._lastEvictedTicks >= this._evictionIntervalTicks) {
this.EvictStaleCacheEntries(ticks);
Volatile.Write(ref this._lastEvictedTicks, ticks);
}
Volatile.Write(ref this._evictLock, 0);
}
return (TValue)orAdd.Value;
}
public void Clear()
{
_cache.Clear();
_lastEvictedTicks = DateTime.UtcNow.Ticks;
}
private void EvictStaleCacheEntries(long utcNowTicks)
{
foreach (KeyValuePair<TKey, CacheEntry> item in _cache) {
if (utcNowTicks - Volatile.Read(ref item.Value.LastUsedTicks) >= _slidingExpirationTicks)
_cache.TryRemove(item.Key, out CacheEntry _);
}
}
}
private static readonly ReflectionEmitMemberAccessor s_sourceAccessor = new ReflectionEmitMemberAccessor();
private static readonly Cache<(string id, Type declaringType, MemberInfo member)> s_cache = new Cache<(string, Type, MemberInfo)>(TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(200));
public static void Clear()
{
s_cache.Clear();
}
public override Action<TCollection, object> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>()
{
return s_cache.GetOrAdd(("CreateAddMethodDelegate", typeof(TCollection), null), ((string id, Type declaringType, MemberInfo member) _) => s_sourceAccessor.CreateAddMethodDelegate<TCollection>());
}
public override Func<object> CreateConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type classType)
{
return s_cache.GetOrAdd(("CreateConstructor", classType, null), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreateConstructor(key.declaringType));
}
public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo)
{
return s_cache.GetOrAdd(("CreateFieldGetter", typeof(TProperty), fieldInfo), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreateFieldGetter<TProperty>((FieldInfo)key.member));
}
public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo)
{
return s_cache.GetOrAdd(("CreateFieldSetter", typeof(TProperty), fieldInfo), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreateFieldSetter<TProperty>((FieldInfo)key.member));
}
[RequiresUnreferencedCode("System.Collections.Immutable converters use Reflection to find and create Immutable Collection types, which requires unreferenced code.")]
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>()
{
return s_cache.GetOrAdd(("CreateImmutableDictionaryCreateRangeDelegate", typeof((TCollection, TKey, TValue)), null), ((string id, Type declaringType, MemberInfo member) _) => s_sourceAccessor.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>());
}
[RequiresUnreferencedCode("System.Collections.Immutable converters use Reflection to find and create Immutable Collection types, which requires unreferenced code.")]
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
{
return s_cache.GetOrAdd(("CreateImmutableEnumerableCreateRangeDelegate", typeof((TCollection, TElement)), null), ((string id, Type declaringType, MemberInfo member) _) => s_sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>());
}
public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
{
return s_cache.GetOrAdd(("CreateParameterizedConstructor", typeof(T), constructor), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member));
}
public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3> CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor)
{
return s_cache.GetOrAdd(("CreateParameterizedConstructor", typeof(T), constructor), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>((ConstructorInfo)key.member));
}
public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo)
{
return s_cache.GetOrAdd(("CreatePropertyGetter", typeof(TProperty), propertyInfo), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member));
}
public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo)
{
return s_cache.GetOrAdd(("CreatePropertySetter", typeof(TProperty), propertyInfo), ((string id, Type declaringType, MemberInfo member) key) => s_sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member));
}
}
}