< Summary

Line coverage
26%
Covered lines: 129
Uncovered lines: 350
Coverable lines: 479
Total lines: 726
Line coverage: 26.9%
Branch coverage
28%
Covered branches: 28
Total branches: 100
Branch coverage: 28%
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/ReflectionEmitMemberAccessor.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
 4#if NETFRAMEWORK || NET
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Reflection;
 9using System.Reflection.Emit;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 14    [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 15    internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
 16    {
 117        public ReflectionEmitMemberAccessor()
 118        {
 119        }
 20
 21        public override Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? constructorInfo)
 107122        {
 107123            Debug.Assert(type != null);
 107124            Debug.Assert(constructorInfo is null || constructorInfo.GetParameters().Length == 0);
 25
 107126            if (type.IsAbstract)
 2027            {
 2028                return null;
 29            }
 30
 105131            if (constructorInfo is null && !type.IsValueType)
 38732            {
 38733                return null;
 34            }
 35
 66436            var dynamicMethod = new DynamicMethod(
 66437                ConstructorInfo.ConstructorName,
 66438                typeof(object),
 66439                Type.EmptyTypes,
 66440                typeof(ReflectionEmitMemberAccessor).Module,
 66441                skipVisibility: true);
 42
 66443            ILGenerator generator = dynamicMethod.GetILGenerator();
 44
 66445            if (constructorInfo is null)
 27046            {
 27047                Debug.Assert(type.IsValueType);
 48
 27049                LocalBuilder local = generator.DeclareLocal(type);
 50
 27051                generator.Emit(OpCodes.Ldloca_S, local);
 27052                generator.Emit(OpCodes.Initobj, type);
 27053                generator.Emit(OpCodes.Ldloc, local);
 27054                generator.Emit(OpCodes.Box, type);
 27055            }
 56            else
 39457            {
 39458                generator.Emit(OpCodes.Newobj, constructorInfo);
 39459                if (type.IsValueType)
 060                {
 61                    // Since C# 10 it's now possible to have parameterless constructors in structs
 062                    generator.Emit(OpCodes.Box, type);
 063                }
 39464            }
 65
 66466            generator.Emit(OpCodes.Ret);
 67
 66468            return CreateDelegate<Func<object>>(dynamicMethod);
 107169        }
 70
 71        public override Func<object?[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
 32672            CreateDelegate<Func<object?[], T>>(CreateParameterizedConstructor(constructor));
 73
 74        private static DynamicMethod CreateParameterizedConstructor(ConstructorInfo constructor)
 32675        {
 32676            Type? type = constructor.DeclaringType;
 77
 32678            Debug.Assert(type != null);
 32679            Debug.Assert(!type.IsAbstract);
 32680            Debug.Assert(!constructor.IsStatic);
 81
 32682            ParameterInfo[] parameters = constructor.GetParameters();
 32683            int parameterCount = parameters.Length;
 84
 32685            var dynamicMethod = new DynamicMethod(
 32686                ConstructorInfo.ConstructorName,
 32687                type,
 32688                new[] { typeof(object[]) },
 32689                typeof(ReflectionEmitMemberAccessor).Module,
 32690                skipVisibility: true);
 91
 32692            ILGenerator generator = dynamicMethod.GetILGenerator();
 93
 94            // For byref parameters, we need to store values in local variables and pass addresses.
 95            // For out parameters, we just need a default-initialized local to pass by address.
 32696            LocalBuilder?[] locals = new LocalBuilder?[parameterCount];
 97
 98            // Track the mapping from constructor parameter index to args[] index.
 99            // out parameters don't have entries in args[].
 326100            int argsIndex = 0;
 326101            int[] argsIndices = new int[parameterCount];
 4564102            for (int i = 0; i < parameterCount; i++)
 1956103            {
 1956104                if (parameters[i].IsOut)
 0105                {
 0106                    argsIndices[i] = -1; // out parameters don't have an args entry
 0107                }
 108                else
 1956109                {
 1956110                    argsIndices[i] = argsIndex++;
 1956111                }
 1956112            }
 113
 4564114            for (int i = 0; i < parameterCount; i++)
 1956115            {
 1956116                Type paramType = parameters[i].ParameterType;
 1956117                if (paramType.IsByRef)
 0118                {
 119                    // Declare a local for the underlying type.
 0120                    Type elementType = paramType.GetElementType()!;
 0121                    locals[i] = generator.DeclareLocal(elementType);
 122
 0123                    if (parameters[i].IsOut)
 0124                    {
 125                        // For out parameters, just initialize the local to default.
 126                        // We don't load from args[] since out params aren't in the metadata.
 0127                        if (elementType.IsValueType)
 0128                        {
 0129                            generator.Emit(OpCodes.Ldloca, locals[i]!);
 0130                            generator.Emit(OpCodes.Initobj, elementType);
 0131                        }
 132                        else
 0133                        {
 0134                            generator.Emit(OpCodes.Ldnull);
 0135                            generator.Emit(OpCodes.Stloc, locals[i]!);
 0136                        }
 0137                    }
 138                    else
 0139                    {
 140                        // Load value from object array, unbox it, and store in the local.
 0141                        generator.Emit(OpCodes.Ldarg_0);
 0142                        generator.Emit(OpCodes.Ldc_I4, argsIndices[i]);
 0143                        generator.Emit(OpCodes.Ldelem_Ref);
 0144                        generator.Emit(OpCodes.Unbox_Any, elementType);
 0145                        generator.Emit(OpCodes.Stloc, locals[i]!);
 0146                    }
 0147                }
 1956148            }
 149
 150            // Now push all arguments onto the stack.
 4564151            for (int i = 0; i < parameterCount; i++)
 1956152            {
 1956153                Type paramType = parameters[i].ParameterType;
 154
 1956155                if (paramType.IsByRef)
 0156                {
 157                    // Load address of the local variable.
 0158                    generator.Emit(OpCodes.Ldloca, locals[i]!);
 0159                }
 160                else
 1956161                {
 162                    // Load value from object array and unbox.
 1956163                    generator.Emit(OpCodes.Ldarg_0);
 1956164                    generator.Emit(OpCodes.Ldc_I4, argsIndices[i]);
 1956165                    generator.Emit(OpCodes.Ldelem_Ref);
 1956166                    generator.Emit(OpCodes.Unbox_Any, paramType);
 1956167                }
 1956168            }
 169
 326170            generator.Emit(OpCodes.Newobj, constructor);
 326171            generator.Emit(OpCodes.Ret);
 172
 326173            return dynamicMethod;
 326174        }
 175
 176        public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>?
 177            CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor) =>
 0178            CreateDelegate<JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>>(
 0179                CreateParameterizedConstructor(constructor, typeof(TArg0), typeof(TArg1), typeof(TArg2), typeof(TArg3)))
 180
 181        public override Func<object?, T> CreateSingleParameterConstructor<T>(ConstructorInfo constructor) =>
 0182            CreateDelegate<Func<object?, T>>(CreateSingleParameterConstructor(constructor));
 183
 184        private static DynamicMethod CreateSingleParameterConstructor(ConstructorInfo constructor)
 0185        {
 0186            Type? type = constructor.DeclaringType;
 187
 0188            Debug.Assert(type != null);
 0189            Debug.Assert(!type.IsAbstract);
 0190            Debug.Assert(!constructor.IsStatic);
 191
 0192            ParameterInfo[] parameters = constructor.GetParameters();
 0193            Debug.Assert(parameters.Length == 1);
 194
 0195            Type parameterType = parameters[0].ParameterType;
 0196            Debug.Assert(!parameterType.IsByRef);
 197
 0198            var dynamicMethod = new DynamicMethod(
 0199                ConstructorInfo.ConstructorName,
 0200                type,
 0201                new[] { typeof(object) },
 0202                typeof(ReflectionEmitMemberAccessor).Module,
 0203                skipVisibility: true);
 204
 0205            ILGenerator generator = dynamicMethod.GetILGenerator();
 0206            EmitObjectArgument(generator, parameterType);
 0207            generator.Emit(OpCodes.Newobj, constructor);
 0208            generator.Emit(OpCodes.Ret);
 209
 0210            return dynamicMethod;
 0211        }
 212
 213        private static void EmitObjectArgument(ILGenerator generator, Type parameterType)
 0214        {
 0215            Type? nullableUnderlyingType = Nullable.GetUnderlyingType(parameterType);
 0216            if (nullableUnderlyingType is not null)
 0217            {
 0218                Label notNull = generator.DefineLabel();
 0219                Label done = generator.DefineLabel();
 0220                LocalBuilder nullableLocal = generator.DeclareLocal(parameterType);
 221
 0222                generator.Emit(OpCodes.Ldarg_0);
 0223                generator.Emit(OpCodes.Brtrue_S, notNull);
 224
 0225                generator.Emit(OpCodes.Ldloca_S, nullableLocal);
 0226                generator.Emit(OpCodes.Initobj, parameterType);
 0227                generator.Emit(OpCodes.Ldloc, nullableLocal);
 0228                generator.Emit(OpCodes.Br_S, done);
 229
 0230                generator.MarkLabel(notNull);
 0231                generator.Emit(OpCodes.Ldarg_0);
 0232                generator.Emit(OpCodes.Unbox_Any, nullableUnderlyingType);
 0233                generator.Emit(OpCodes.Newobj, parameterType.GetConstructor(new[] { nullableUnderlyingType })!);
 234
 0235                generator.MarkLabel(done);
 0236            }
 237            else
 0238            {
 0239                generator.Emit(OpCodes.Ldarg_0);
 0240                generator.Emit(parameterType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, parameterType);
 0241            }
 0242        }
 243
 244        private static DynamicMethod? CreateParameterizedConstructor(ConstructorInfo constructor, Type parameterType1, T
 0245        {
 0246            Type? type = constructor.DeclaringType;
 247
 0248            Debug.Assert(type != null);
 0249            Debug.Assert(!type.IsAbstract);
 0250            Debug.Assert(!constructor.IsStatic);
 251
 0252            ParameterInfo[] parameters = constructor.GetParameters();
 0253            int parameterCount = parameters.Length;
 254
 0255            var dynamicMethod = new DynamicMethod(
 0256                ConstructorInfo.ConstructorName,
 0257                type,
 0258                new[] { parameterType1, parameterType2, parameterType3, parameterType4 },
 0259                typeof(ReflectionEmitMemberAccessor).Module,
 0260                skipVisibility: true);
 261
 0262            ILGenerator generator = dynamicMethod.GetILGenerator();
 263
 264            // For byref parameters, we need locals because we receive values but need to pass addresses.
 0265            LocalBuilder?[] locals = new LocalBuilder?[parameterCount];
 0266            for (int index = 0; index < parameterCount; index++)
 0267            {
 0268                if (parameters[index].ParameterType.IsByRef)
 0269                {
 0270                    Type elementType = parameters[index].ParameterType.GetElementType()!;
 0271                    locals[index] = generator.DeclareLocal(elementType);
 272
 0273                    if (parameters[index].IsOut)
 0274                    {
 275                        // For out parameters, initialize to default (the caller passes placeholder values).
 0276                        if (elementType.IsValueType)
 0277                        {
 0278                            generator.Emit(OpCodes.Ldloca, locals[index]!);
 0279                            generator.Emit(OpCodes.Initobj, elementType);
 0280                        }
 281                        else
 0282                        {
 0283                            generator.Emit(OpCodes.Ldnull);
 0284                            generator.Emit(OpCodes.Stloc, locals[index]!);
 0285                        }
 0286                    }
 287                    else
 0288                    {
 289                        // For in/ref parameters, copy the argument value to the local.
 0290                        generator.Emit(
 0291                            index switch
 0292                            {
 0293                                0 => OpCodes.Ldarg_0,
 0294                                1 => OpCodes.Ldarg_1,
 0295                                2 => OpCodes.Ldarg_2,
 0296                                3 => OpCodes.Ldarg_3,
 0297                                _ => throw new InvalidOperationException()
 0298                            });
 0299                        generator.Emit(OpCodes.Stloc, locals[index]!);
 0300                    }
 0301                }
 0302            }
 303
 304            // Now push all arguments onto the stack.
 0305            for (int index = 0; index < parameterCount; index++)
 0306            {
 0307                Debug.Assert(index <= JsonConstants.UnboxedParameterCountThreshold);
 308
 0309                if (parameters[index].ParameterType.IsByRef)
 0310                {
 311                    // Load address of the local variable.
 0312                    generator.Emit(OpCodes.Ldloca, locals[index]!);
 0313                }
 314                else
 0315                {
 316                    // Load the argument value directly.
 0317                    generator.Emit(
 0318                        index switch
 0319                        {
 0320                            0 => OpCodes.Ldarg_0,
 0321                            1 => OpCodes.Ldarg_1,
 0322                            2 => OpCodes.Ldarg_2,
 0323                            3 => OpCodes.Ldarg_3,
 0324                            _ => throw new InvalidOperationException()
 0325                        });
 0326                }
 0327            }
 328
 0329            generator.Emit(OpCodes.Newobj, constructor);
 0330            generator.Emit(OpCodes.Ret);
 331
 0332            return dynamicMethod;
 0333        }
 334
 335        public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAcce
 0336            CreateDelegate<Action<TCollection, object?>>(CreateAddMethodDelegate(typeof(TCollection)));
 337
 338        private static DynamicMethod CreateAddMethodDelegate(
 339            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type collectionType)
 0340        {
 341            // We verified this won't be null when we created the converter that calls this method.
 0342            MethodInfo realMethod = (collectionType.GetMethod("Push") ?? collectionType.GetMethod("Enqueue"))!;
 343
 0344            var dynamicMethod = new DynamicMethod(
 0345                realMethod.Name,
 0346                typeof(void),
 0347                new[] { collectionType, typeof(object) },
 0348                typeof(ReflectionEmitMemberAccessor).Module,
 0349                skipVisibility: true);
 350
 0351            ILGenerator generator = dynamicMethod.GetILGenerator();
 352
 0353            generator.Emit(OpCodes.Ldarg_0);
 0354            generator.Emit(OpCodes.Ldarg_1);
 0355            generator.Emit(OpCodes.Callvirt, realMethod);
 0356            generator.Emit(OpCodes.Ret);
 357
 0358            return dynamicMethod;
 0359        }
 360
 361        public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollectio
 0362            CreateDelegate<Func<IEnumerable<TElement>, TCollection>>(
 0363                CreateImmutableEnumerableCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<T
 364
 365        private static DynamicMethod CreateImmutableEnumerableCreateRangeDelegate(Type collectionType, Type elementType,
 0366        {
 0367            MethodInfo realMethod = collectionType.GetImmutableEnumerableCreateRangeMethod(elementType);
 368
 0369            var dynamicMethod = new DynamicMethod(
 0370                realMethod.Name,
 0371                collectionType,
 0372                new[] { enumerableType },
 0373                typeof(ReflectionEmitMemberAccessor).Module,
 0374                skipVisibility: true);
 375
 0376            ILGenerator generator = dynamicMethod.GetILGenerator();
 377
 0378            generator.Emit(OpCodes.Ldarg_0);
 0379            generator.Emit(OpCodes.Call, realMethod);
 0380            generator.Emit(OpCodes.Ret);
 381
 0382            return dynamicMethod;
 0383        }
 384
 385        public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeD
 0386            CreateDelegate<Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>>(
 0387                CreateImmutableDictionaryCreateRangeDelegate(typeof(TCollection), typeof(TKey), typeof(TValue), typeof(I
 388
 389        private static DynamicMethod CreateImmutableDictionaryCreateRangeDelegate(Type collectionType, Type keyType, Typ
 0390        {
 0391            MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(keyType, valueType);
 392
 0393            var dynamicMethod = new DynamicMethod(
 0394                realMethod.Name,
 0395                collectionType,
 0396                new[] { enumerableType },
 0397                typeof(ReflectionEmitMemberAccessor).Module,
 0398                skipVisibility: true);
 399
 0400            ILGenerator generator = dynamicMethod.GetILGenerator();
 401
 0402            generator.Emit(OpCodes.Ldarg_0);
 0403            generator.Emit(OpCodes.Call, realMethod);
 0404            generator.Emit(OpCodes.Ret);
 405
 0406            return dynamicMethod;
 0407        }
 408
 409        public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
 2904410            CreateDelegate<Func<object, TProperty>>(CreatePropertyGetter(propertyInfo, typeof(TProperty)));
 411
 412        public override Func<TDeclaringType, TProperty> CreatePropertyGetter<TDeclaringType, TProperty>(PropertyInfo pro
 0413            CreateDelegate<Func<TDeclaringType, TProperty>>(CreatePropertyGetter(propertyInfo, typeof(TDeclaringType), t
 414
 415        private static DynamicMethod CreatePropertyGetter(PropertyInfo propertyInfo, Type runtimePropertyType)
 2904416        {
 2904417            MethodInfo? realMethod = propertyInfo.GetMethod;
 2904418            Debug.Assert(realMethod != null);
 419
 2904420            Type? declaringType = propertyInfo.DeclaringType;
 2904421            Debug.Assert(declaringType != null);
 422
 2904423            Type declaredPropertyType = propertyInfo.PropertyType;
 424
 2904425            DynamicMethod dynamicMethod = CreateGetterMethod(propertyInfo.Name, runtimePropertyType);
 2904426            ILGenerator generator = dynamicMethod.GetILGenerator();
 427
 2904428            generator.Emit(OpCodes.Ldarg_0);
 429
 2904430            if (declaringType.IsValueType)
 0431            {
 0432                generator.Emit(OpCodes.Unbox, declaringType);
 0433                generator.Emit(OpCodes.Call, realMethod);
 0434            }
 435            else
 2904436            {
 2904437                generator.Emit(OpCodes.Castclass, declaringType);
 2904438                generator.Emit(OpCodes.Callvirt, realMethod);
 2904439            }
 440
 441            // declaredPropertyType: Type of the property
 442            // runtimePropertyType:  <T> of JsonConverter / JsonPropertyInfo
 443
 2904444            if (declaredPropertyType != runtimePropertyType && declaredPropertyType.IsValueType)
 0445            {
 446                // Not supported scenario: possible if declaredPropertyType == int? and runtimePropertyType == int
 447                // We should catch that particular case earlier in converter generation.
 0448                Debug.Assert(!runtimePropertyType.IsValueType);
 449
 0450                generator.Emit(OpCodes.Box, declaredPropertyType);
 0451            }
 452
 2904453            generator.Emit(OpCodes.Ret);
 454
 2904455            return dynamicMethod;
 2904456        }
 457
 458        private static DynamicMethod CreatePropertyGetter(PropertyInfo propertyInfo, Type declaringType, Type runtimePro
 0459        {
 0460            MethodInfo? realMethod = propertyInfo.GetMethod;
 0461            Debug.Assert(realMethod != null);
 462
 0463            Type? propertyDeclaringType = propertyInfo.DeclaringType;
 0464            Debug.Assert(propertyDeclaringType != null);
 0465            Debug.Assert(propertyDeclaringType.IsAssignableFrom(declaringType));
 466
 0467            Type declaredPropertyType = propertyInfo.PropertyType;
 468
 0469            DynamicMethod dynamicMethod = CreateGetterMethod(propertyInfo.Name, declaringType, runtimePropertyType);
 0470            ILGenerator generator = dynamicMethod.GetILGenerator();
 471
 0472            if (declaringType.IsValueType)
 0473            {
 0474                generator.Emit(OpCodes.Ldarga_S, 0);
 0475                if (propertyDeclaringType.IsInterface)
 0476                {
 0477                    generator.Emit(OpCodes.Constrained, declaringType);
 0478                    generator.Emit(OpCodes.Callvirt, realMethod);
 0479                }
 480                else
 0481                {
 0482                    generator.Emit(OpCodes.Call, realMethod);
 0483                }
 0484            }
 485            else
 0486            {
 0487                generator.Emit(OpCodes.Ldarg_0);
 488
 0489                if (declaringType != propertyDeclaringType)
 0490                {
 0491                    generator.Emit(OpCodes.Castclass, propertyDeclaringType);
 0492                }
 493
 0494                generator.Emit(OpCodes.Callvirt, realMethod);
 0495            }
 496
 0497            if (declaredPropertyType != runtimePropertyType && declaredPropertyType.IsValueType)
 0498            {
 0499                Debug.Assert(!runtimePropertyType.IsValueType);
 500
 0501                generator.Emit(OpCodes.Box, declaredPropertyType);
 0502            }
 503
 0504            generator.Emit(OpCodes.Ret);
 505
 0506            return dynamicMethod;
 0507        }
 508
 509        public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
 2904510            CreateDelegate<Action<object, TProperty>>(CreatePropertySetter(propertyInfo, typeof(TProperty)));
 511
 512        private static DynamicMethod CreatePropertySetter(PropertyInfo propertyInfo, Type runtimePropertyType)
 2904513        {
 2904514            MethodInfo? realMethod = propertyInfo.SetMethod;
 2904515            Debug.Assert(realMethod != null);
 516
 2904517            Type? declaringType = propertyInfo.DeclaringType;
 2904518            Debug.Assert(declaringType != null);
 519
 2904520            Type declaredPropertyType = propertyInfo.PropertyType;
 521
 2904522            DynamicMethod dynamicMethod = CreateSetterMethod(propertyInfo.Name, runtimePropertyType);
 2904523            ILGenerator generator = dynamicMethod.GetILGenerator();
 524
 2904525            generator.Emit(OpCodes.Ldarg_0);
 2904526            generator.Emit(declaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, declaringType);
 2904527            generator.Emit(OpCodes.Ldarg_1);
 528
 529            // declaredPropertyType: Type of the property
 530            // runtimePropertyType:  <T> of JsonConverter / JsonPropertyInfo
 531
 2904532            if (declaredPropertyType != runtimePropertyType && declaredPropertyType.IsValueType)
 0533            {
 534                // Not supported scenario: possible if e.g. declaredPropertyType == int? and runtimePropertyType == int
 535                // We should catch that particular case earlier in converter generation.
 0536                Debug.Assert(!runtimePropertyType.IsValueType);
 537
 0538                generator.Emit(OpCodes.Unbox_Any, declaredPropertyType);
 0539            }
 540
 2904541            generator.Emit(declaringType.IsValueType ? OpCodes.Call : OpCodes.Callvirt, realMethod);
 2904542            generator.Emit(OpCodes.Ret);
 543
 2904544            return dynamicMethod;
 2904545        }
 546
 547        public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
 0548            CreateDelegate<Func<object, TProperty>>(CreateFieldGetter(fieldInfo, typeof(TProperty)));
 549
 550        private static DynamicMethod CreateFieldGetter(FieldInfo fieldInfo, Type runtimeFieldType)
 0551        {
 0552            Type? declaringType = fieldInfo.DeclaringType;
 0553            Debug.Assert(declaringType != null);
 554
 0555            Type declaredFieldType = fieldInfo.FieldType;
 556
 0557            DynamicMethod dynamicMethod = CreateGetterMethod(fieldInfo.Name, runtimeFieldType);
 0558            ILGenerator generator = dynamicMethod.GetILGenerator();
 559
 0560            generator.Emit(OpCodes.Ldarg_0);
 0561            generator.Emit(
 0562                declaringType.IsValueType
 0563                    ? OpCodes.Unbox
 0564                    : OpCodes.Castclass,
 0565                declaringType);
 0566            generator.Emit(OpCodes.Ldfld, fieldInfo);
 567
 568            // declaredFieldType: Type of the field
 569            // runtimeFieldType:  <T> of JsonConverter / JsonPropertyInfo
 570
 0571            if (declaredFieldType.IsValueType && declaredFieldType != runtimeFieldType)
 0572            {
 0573                generator.Emit(OpCodes.Box, declaredFieldType);
 0574            }
 575
 0576            generator.Emit(OpCodes.Ret);
 577
 0578            return dynamicMethod;
 0579        }
 580
 581        public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo) =>
 0582            CreateDelegate<Action<object, TProperty>>(CreateFieldSetter(fieldInfo, typeof(TProperty)));
 583
 584        private static DynamicMethod CreateFieldSetter(FieldInfo fieldInfo, Type runtimeFieldType)
 0585        {
 0586            Type? declaringType = fieldInfo.DeclaringType;
 0587            Debug.Assert(declaringType != null);
 588
 0589            Type declaredFieldType = fieldInfo.FieldType;
 590
 0591            DynamicMethod dynamicMethod = CreateSetterMethod(fieldInfo.Name, runtimeFieldType);
 0592            ILGenerator generator = dynamicMethod.GetILGenerator();
 593
 0594            generator.Emit(OpCodes.Ldarg_0);
 0595            generator.Emit(declaringType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, declaringType);
 0596            generator.Emit(OpCodes.Ldarg_1);
 597
 598            // declaredFieldType: Type of the field
 599            // runtimeFieldType:  <T> of JsonConverter / JsonPropertyInfo
 600
 0601            if (declaredFieldType != runtimeFieldType && declaredFieldType.IsValueType)
 0602            {
 0603                generator.Emit(OpCodes.Unbox_Any, declaredFieldType);
 0604            }
 605
 0606            generator.Emit(OpCodes.Stfld, fieldInfo);
 0607            generator.Emit(OpCodes.Ret);
 608
 0609            return dynamicMethod;
 0610        }
 611
 612        public override UnionTryGetValueAccessor<TUnion> CreateUnionTryGetValueAccessor<TUnion>(IReadOnlyList<KeyValuePa
 0613            CreateDelegate<UnionTryGetValueAccessor<TUnion>>(CreateUnionTryGetValueAccessor(typeof(TUnion), entries));
 614
 615        private static DynamicMethod CreateUnionTryGetValueAccessor(Type unionType, IReadOnlyList<KeyValuePair<Type, Met
 0616        {
 0617            Debug.Assert(entries.Count > 0, "Should not build a chained accessor for an empty entry list.");
 618
 619            // Emit a single DynamicMethod that chains the user-declared
 620            // 'bool TUnion.TryGetValue(out CaseType)' overloads in caller-supplied order.
 621            // First successful overload sets the out parameters and returns true; if none
 622            // match, the trailer writes null/null and returns false. Producing one piece of
 623            // IL avoids per-entry closure or delegate hop and keeps the dispatch O(chainLength)
 624            // with no managed allocations on the hot path.
 0625            DynamicMethod dynamicMethod = new DynamicMethod(
 0626                "UnionTryGetValueAccessor",
 0627                typeof(bool),
 0628                new[] { unionType, typeof(Type).MakeByRefType(), typeof(object).MakeByRefType() },
 0629                typeof(ReflectionEmitMemberAccessor).Module,
 0630                skipVisibility: true);
 631
 0632            ILGenerator generator = dynamicMethod.GetILGenerator();
 0633            bool unionIsValueType = unionType.IsValueType;
 634
 0635            for (int i = 0; i < entries.Count; i++)
 0636            {
 0637                KeyValuePair<Type, MethodInfo> entry = entries[i];
 0638                Type caseType = entry.Key;
 0639                MethodInfo method = entry.Value;
 0640                LocalBuilder extracted = generator.DeclareLocal(caseType);
 0641                Label nextEntry = generator.DefineLabel();
 642
 643                // if (!union.TryGetValue(out extracted)) goto nextEntry;
 0644                if (unionIsValueType)
 0645                {
 0646                    generator.Emit(OpCodes.Ldarga_S, (byte)0);
 0647                    generator.Emit(OpCodes.Ldloca_S, extracted);
 0648                    generator.Emit(OpCodes.Call, method);
 0649                }
 650                else
 0651                {
 0652                    generator.Emit(OpCodes.Ldarg_0);
 0653                    generator.Emit(OpCodes.Ldloca_S, extracted);
 0654                    generator.Emit(OpCodes.Callvirt, method);
 0655                }
 0656                generator.Emit(OpCodes.Brfalse, nextEntry);
 657
 658                // caseType = typeof(CaseType);
 0659                generator.Emit(OpCodes.Ldarg_1);
 0660                generator.Emit(OpCodes.Ldtoken, caseType);
 0661                generator.Emit(OpCodes.Call, s_getTypeFromHandleMethod);
 0662                generator.Emit(OpCodes.Stind_Ref);
 663
 664                // value = (object?)extracted;
 0665                generator.Emit(OpCodes.Ldarg_2);
 0666                generator.Emit(OpCodes.Ldloc, extracted);
 0667                if (caseType.IsValueType)
 0668                {
 0669                    generator.Emit(OpCodes.Box, caseType);
 0670                }
 0671                generator.Emit(OpCodes.Stind_Ref);
 672
 673                // return true;
 0674                generator.Emit(OpCodes.Ldc_I4_1);
 0675                generator.Emit(OpCodes.Ret);
 676
 0677                generator.MarkLabel(nextEntry);
 0678            }
 679
 680            // No overload matched: caseType = null; value = null; return false;
 0681            generator.Emit(OpCodes.Ldarg_1);
 0682            generator.Emit(OpCodes.Ldnull);
 0683            generator.Emit(OpCodes.Stind_Ref);
 0684            generator.Emit(OpCodes.Ldarg_2);
 0685            generator.Emit(OpCodes.Ldnull);
 0686            generator.Emit(OpCodes.Stind_Ref);
 0687            generator.Emit(OpCodes.Ldc_I4_0);
 0688            generator.Emit(OpCodes.Ret);
 689
 0690            return dynamicMethod;
 0691        }
 692
 0693        private static readonly MethodInfo s_getTypeFromHandleMethod =
 0694            typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) })!;
 695
 696        private static DynamicMethod CreateGetterMethod(string memberName, Type memberType) =>
 2904697            new DynamicMethod(
 2904698                memberName + "Getter",
 2904699                memberType,
 2904700                new[] { typeof(object) },
 2904701                typeof(ReflectionEmitMemberAccessor).Module,
 2904702                skipVisibility: true);
 703
 704        private static DynamicMethod CreateGetterMethod(string memberName, Type declaringType, Type memberType) =>
 0705            new DynamicMethod(
 0706                memberName + "Getter",
 0707                memberType,
 0708                new[] { declaringType },
 0709                typeof(ReflectionEmitMemberAccessor).Module,
 0710                skipVisibility: true);
 711
 712        private static DynamicMethod CreateSetterMethod(string memberName, Type memberType) =>
 2904713            new DynamicMethod(
 2904714                memberName + "Setter",
 2904715                typeof(void),
 2904716                new[] { typeof(object), memberType },
 2904717                typeof(ReflectionEmitMemberAccessor).Module,
 2904718                skipVisibility: true);
 719
 720        [return: NotNullIfNotNull(nameof(method))]
 721        private static T? CreateDelegate<T>(DynamicMethod? method) where T : Delegate =>
 6798722            (T?)method?.CreateDelegate(typeof(T));
 723    }
 724}
 725#endif
 726

Methods/Properties

.ctor()
CreateParameterlessConstructor(System.Type,System.Reflection.ConstructorInfo)
CreateParameterizedConstructor(System.Reflection.ConstructorInfo)
CreateParameterizedConstructor(System.Reflection.ConstructorInfo)
CreateParameterizedConstructor(System.Reflection.ConstructorInfo)
CreateSingleParameterConstructor(System.Reflection.ConstructorInfo)
CreateSingleParameterConstructor(System.Reflection.ConstructorInfo)
EmitObjectArgument(System.Reflection.Emit.ILGenerator,System.Type)
CreateParameterizedConstructor(System.Reflection.ConstructorInfo,System.Type,System.Type,System.Type,System.Type)
CreateAddMethodDelegate()
CreateAddMethodDelegate(System.Type)
CreateImmutableEnumerableCreateRangeDelegate()
CreateImmutableEnumerableCreateRangeDelegate(System.Type,System.Type,System.Type)
CreateImmutableDictionaryCreateRangeDelegate()
CreateImmutableDictionaryCreateRangeDelegate(System.Type,System.Type,System.Type,System.Type)
CreatePropertyGetter(System.Reflection.PropertyInfo)
CreatePropertyGetter(System.Reflection.PropertyInfo)
CreatePropertyGetter(System.Reflection.PropertyInfo,System.Type)
CreatePropertyGetter(System.Reflection.PropertyInfo,System.Type,System.Type)
CreatePropertySetter(System.Reflection.PropertyInfo)
CreatePropertySetter(System.Reflection.PropertyInfo,System.Type)
CreateFieldGetter(System.Reflection.FieldInfo)
CreateFieldGetter(System.Reflection.FieldInfo,System.Type)
CreateFieldSetter(System.Reflection.FieldInfo)
CreateFieldSetter(System.Reflection.FieldInfo,System.Type)
CreateUnionTryGetValueAccessor(System.Collections.Generic.IReadOnlyList`1<System.Collections.Generic.KeyValuePair`2<System.Type,System.Reflection.MethodInfo>>)
CreateUnionTryGetValueAccessor(System.Type,System.Collections.Generic.IReadOnlyList`1<System.Collections.Generic.KeyValuePair`2<System.Type,System.Reflection.MethodInfo>>)
.cctor()
CreateGetterMethod(System.String,System.Type)
CreateGetterMethod(System.String,System.Type,System.Type)
CreateSetterMethod(System.String,System.Type)
CreateDelegate(System.Reflection.Emit.DynamicMethod)