< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 182
Coverable lines: 182
Total lines: 308
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 36
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8using System.Text.Json.Reflection;
 9
 10namespace System.Text.Json.Serialization.Metadata
 11{
 12    [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 13    [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 14    internal sealed class ReflectionMemberAccessor : MemberAccessor
 15    {
 016        public ReflectionMemberAccessor()
 017        {
 018        }
 19
 20        public override Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? ctorInfo)
 021        {
 022            Debug.Assert(type != null);
 023            Debug.Assert(ctorInfo is null || ctorInfo.GetParameters().Length == 0);
 24
 025            if (type.IsAbstract)
 026            {
 027                return null;
 28            }
 29
 030            if (ctorInfo is null)
 031            {
 032                return type.IsValueType
 033                    ? () => Activator.CreateInstance(type, nonPublic: false)!
 034                    : null;
 35            }
 36
 37#if NET
 038            ConstructorInvoker invoker = ConstructorInvoker.Create(ctorInfo);
 039            return invoker.Invoke;
 40#else
 41            return () => ctorInfo.InvokeNoWrapExceptions(null);
 42#endif
 043        }
 44
 45        public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor)
 046        {
 047            Type type = typeof(T);
 48
 049            Debug.Assert(!type.IsAbstract);
 050            Debug.Assert(constructor.DeclaringType == type && !constructor.IsStatic);
 51
 052            int parameterCount = constructor.GetParameters().Length;
 53
 54#if NET
 055            ConstructorInvoker invoker = ConstructorInvoker.Create(constructor);
 056            return arguments => (T)invoker.Invoke(arguments.AsSpan(0, parameterCount));
 57#else
 58            return (arguments) =>
 59            {
 60                // The input array was rented from the shared ArrayPool, so its size is likely to be larger than the par
 61                // The emit equivalent of this method does not (need to) allocate here + transfer the objects.
 62                object[] argsToPass = new object[parameterCount];
 63
 64                Array.Copy(arguments, 0, argsToPass, 0, parameterCount);
 65
 66                // Not wrapping in TargetInvocationException also plumbs ArgumentException through for
 67                // tuples with more than 7 generic parameters, e.g.
 68                // System.ArgumentException : The last element of an eight element tuple must be a Tuple.
 69                return (T)constructor.InvokeNoWrapExceptions(argsToPass);
 70            };
 71#endif
 072        }
 73
 74        public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>?
 75            CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor)
 076        {
 077            Type type = typeof(T);
 78
 079            Debug.Assert(!type.IsAbstract);
 080            Debug.Assert(constructor.DeclaringType == type && !constructor.IsStatic);
 81
 082            int parameterCount = constructor.GetParameters().Length;
 83#if NET
 084            ConstructorInvoker invoker = ConstructorInvoker.Create(constructor);
 85#endif
 86
 087            Debug.Assert(parameterCount <= JsonConstants.UnboxedParameterCountThreshold);
 88
 089            return (arg0, arg1, arg2, arg3) =>
 090            {
 091#if NET
 092                switch (parameterCount)
 093                {
 094                    case 0:
 095                        return (T)invoker.Invoke();
 096                    case 1:
 097                        return (T)invoker.Invoke(arg0);
 098                    case 2:
 099                        return (T)invoker.Invoke(arg0, arg1);
 0100                    case 3:
 0101                        return (T)invoker.Invoke(arg0, arg1, arg2);
 0102                    case 4:
 0103                        return (T)invoker.Invoke(arg0, arg1, arg2, arg3);
 0104                    default:
 0105                        Debug.Fail("We shouldn't be here if there are more than 4 parameters.");
 0106                        throw new InvalidOperationException();
 0107                }
 0108#else
 0109                object?[] arguments = new object?[parameterCount];
 0110
 0111                switch (parameterCount)
 0112                {
 0113                    case > 4:
 0114                        Debug.Fail("We shouldn't be here if there are more than 4 parameters.");
 0115                        throw new InvalidOperationException();
 0116                    case 4:
 0117                        arguments[3] = arg3;
 0118                        goto case 3;
 0119                    case 3:
 0120                        arguments[2] = arg2;
 0121                        goto case 2;
 0122                    case 2:
 0123                        arguments[1] = arg1;
 0124                        goto case 1;
 0125                    case 1:
 0126                        arguments[0] = arg0;
 0127                        break;
 0128                }
 0129
 0130                return (T)constructor.InvokeNoWrapExceptions(arguments);
 0131#endif
 0132            };
 0133        }
 134
 135        public override Func<object?, T> CreateSingleParameterConstructor<T>(ConstructorInfo constructor)
 0136        {
 0137            Type type = typeof(T);
 138
 0139            Debug.Assert(!type.IsAbstract);
 0140            Debug.Assert(constructor.DeclaringType == type && !constructor.IsStatic);
 0141            Debug.Assert(constructor.GetParameters().Length == 1);
 142
 143#if NET
 0144            ConstructorInvoker invoker = ConstructorInvoker.Create(constructor);
 0145            return value => (T)invoker.Invoke(value);
 146#else
 147            return value => (T)constructor.InvokeNoWrapExceptions(new object?[] { value });
 148#endif
 0149        }
 150
 151        public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAcce
 0152        {
 0153            Type collectionType = typeof(TCollection);
 0154            Type elementType = typeof(object);
 155
 156            // We verified this won't be null when we created the converter for the collection type.
 0157            MethodInfo addMethod = (collectionType.GetMethod("Push") ?? collectionType.GetMethod("Enqueue"))!;
 158#if NET
 0159            MethodInvoker invoker = MethodInvoker.Create(addMethod);
 0160            return (collection, element) => invoker.Invoke(collection, element);
 161#else
 162            return (collection, element) => addMethod.InvokeNoWrapExceptions(collection, new object[] { element });
 163#endif
 0164        }
 165
 166        public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollectio
 0167        {
 0168            MethodInfo createRange = typeof(TCollection).GetImmutableEnumerableCreateRangeMethod(typeof(TElement));
 0169            return (Func<IEnumerable<TElement>, TCollection>)createRange.CreateDelegate(
 0170                typeof(Func<IEnumerable<TElement>, TCollection>));
 0171        }
 172
 173        public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeD
 0174        {
 0175            MethodInfo createRange = typeof(TCollection).GetImmutableDictionaryCreateRangeMethod(typeof(TKey), typeof(TV
 0176            return (Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>)createRange.CreateDelegate(
 0177                typeof(Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>));
 0178        }
 179
 180        public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo)
 0181        {
 0182            MethodInfo getMethodInfo = propertyInfo.GetMethod!;
 183#if NET
 0184            MethodInvoker invoker = MethodInvoker.Create(getMethodInfo);
 0185            return obj => (TProperty)invoker.Invoke(obj)!;
 186#else
 187            return obj => (TProperty)getMethodInfo.InvokeNoWrapExceptions(obj, null)!;
 188#endif
 0189        }
 190
 191        private delegate TProperty ValueTypePropertyGetter<TProperty, TDeclaringType>(ref TDeclaringType obj);
 192
 193        public override Func<TDeclaringType, TProperty> CreatePropertyGetter<TDeclaringType, TProperty>(PropertyInfo pro
 0194        {
 0195            MethodInfo getMethodInfo = propertyInfo.GetMethod!;
 196
 197            // If TDeclaringType is a value type, the instance is passed by reference,
 198            // so we need to create a wrapper delegate to account for this.
 0199            if (typeof(TDeclaringType).IsValueType)
 0200            {
 0201                ValueTypePropertyGetter<TProperty, TDeclaringType> f = getMethodInfo.CreateDelegate<ValueTypePropertyGet
 0202                return obj => f(ref obj);
 203            }
 204
 0205            return getMethodInfo.CreateDelegate<Func<TDeclaringType, TProperty>>();
 0206        }
 207
 208        public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo)
 0209        {
 0210            MethodInfo setMethodInfo = propertyInfo.SetMethod!;
 211
 212#if NET
 0213            MethodInvoker invoker = MethodInvoker.Create(setMethodInfo);
 0214            return (obj, value) => invoker.Invoke(obj, value);
 215#else
 216            return (obj, value) => setMethodInfo.InvokeNoWrapExceptions(obj, new object[] { value });
 217#endif
 0218        }
 219
 220        public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
 0221            delegate (object obj)
 0222            {
 0223                return (TProperty)fieldInfo.GetValue(obj)!;
 0224            };
 225
 226        public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo) =>
 0227            delegate (object obj, TProperty value)
 0228            {
 0229                fieldInfo.SetValue(obj, value);
 0230            };
 231
 232        public override UnionTryGetValueAccessor<TUnion> CreateUnionTryGetValueAccessor<TUnion>(IReadOnlyList<KeyValuePa
 0233        {
 234            // Build per-entry typed delegates via Delegate.CreateDelegate so each TryGetValue
 235            // call is a direct invocation rather than MethodInfo.Invoke (which would box
 236            // value-type unions and allocate per call). Then return a closure that walks the
 237            // chain in caller-supplied order; first match wins.
 0238            int count = entries.Count;
 0239            Type[] caseTypes = new Type[count];
 0240            UnionTryGetValueAccessor<TUnion>[] chain = new UnionTryGetValueAccessor<TUnion>[count];
 0241            for (int i = 0; i < count; i++)
 0242            {
 0243                KeyValuePair<Type, MethodInfo> entry = entries[i];
 0244                caseTypes[i] = entry.Key;
 0245                chain[i] = (UnionTryGetValueAccessor<TUnion>)typeof(ReflectionMemberAccessor)
 0246                    .GetMethod(nameof(CreateUnionTryGetValueAccessorCore), BindingFlags.NonPublic | BindingFlags.Static)
 0247                    .MakeGenericMethod(typeof(TUnion), entry.Key)
 0248                    .Invoke(null, new object[] { entry.Value })!;
 0249            }
 250
 0251            return (TUnion union, out Type? caseType, out object? value) =>
 0252            {
 0253                for (int i = 0; i < chain.Length; i++)
 0254                {
 0255                    if (chain[i](union, out _, out value))
 0256                    {
 0257                        caseType = caseTypes[i];
 0258                        return true;
 0259                    }
 0260                }
 0261
 0262                caseType = null;
 0263                value = null;
 0264                return false;
 0265            };
 0266        }
 267
 268        private delegate bool TypedTryGetValueDelegate<TUnion, TCase>(TUnion union, out TCase? value);
 269
 270        private delegate bool TypedStructTryGetValueDelegate<TUnion, TCase>(ref TUnion union, out TCase? value);
 271
 272        private static UnionTryGetValueAccessor<TUnion> CreateUnionTryGetValueAccessorCore<TUnion, TCase>(MethodInfo met
 0273        {
 274            // Per-entry adapter: binds the user-declared method to a typed delegate and
 275            // returns a delegate matching UnionTryGetValueAccessor<TUnion>. The outer chained
 276            // delegate fills in caseType on success; this inner adapter only reports value.
 0277            if (typeof(TUnion).IsValueType)
 0278            {
 0279                TypedStructTryGetValueDelegate<TUnion, TCase> typed =
 0280                    (TypedStructTryGetValueDelegate<TUnion, TCase>)Delegate.CreateDelegate(
 0281                        typeof(TypedStructTryGetValueDelegate<TUnion, TCase>), method, throwOnBindFailure: true)!;
 282
 0283                return (TUnion union, out Type? caseType, out object? value) =>
 0284                {
 0285                    bool result = typed(ref union, out TCase? extracted);
 0286                    caseType = null;
 0287                    value = result ? extracted : null;
 0288                    return result;
 0289                };
 290            }
 291            else
 0292            {
 0293                TypedTryGetValueDelegate<TUnion, TCase> typed =
 0294                    (TypedTryGetValueDelegate<TUnion, TCase>)Delegate.CreateDelegate(
 0295                        typeof(TypedTryGetValueDelegate<TUnion, TCase>), method, throwOnBindFailure: true)!;
 296
 0297                return (TUnion union, out Type? caseType, out object? value) =>
 0298                {
 0299                    bool result = typed(union, out TCase? extracted);
 0300                    caseType = null;
 0301                    value = result ? extracted : null;
 0302                    return result;
 0303                };
 304            }
 0305        }
 306    }
 307}
 308