File: System\Runtime\Serialization\AccessorBuilder.cs
Web Access
Project: src\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.DataContracts;
using System.Text;
using System.Threading.Tasks;
 
namespace System.Runtime.Serialization
{
    internal static class FastInvokerBuilder
    {
        public delegate void Setter(ref object obj, object? value);
        public delegate object? Getter(object obj);
 
        private delegate void StructSetDelegate<T, TArg>(ref T obj, TArg value);
        private delegate TResult StructGetDelegate<T, out TResult>(ref T obj);
 
        private static readonly MethodInfo s_createGetterInternal = typeof(FastInvokerBuilder).GetMethod(nameof(CreateGetterInternal), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)!;
        private static readonly MethodInfo s_createSetterInternal = typeof(FastInvokerBuilder).GetMethod(nameof(CreateSetterInternal), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)!;
        private static readonly MethodInfo s_make = typeof(FastInvokerBuilder).GetMethod(nameof(Make), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)!;
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
            Justification = "The call to MakeGenericMethod is safe due to the fact that we are preserving the constructors of type which is what Make() is doing.")]
        public static Func<object> GetMakeNewInstanceFunc(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
            Type type)
        {
            Func<object> make = s_make.MakeGenericMethod(type).CreateDelegate<Func<object>>();
            return make;
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
            Justification = "The call to MakeGenericMethod is safe due to the fact that FastInvokerBuilder.CreateGetterInternal<T, T1> is not annotated.")]
        public static Getter CreateGetter(MemberInfo memberInfo)
        {
            if (memberInfo is PropertyInfo propInfo)
            {
                Type declaringType = propInfo.DeclaringType!;
                Type propertyType = propInfo.PropertyType!;
 
                if (declaringType.IsGenericType && declaringType.GetGenericTypeDefinition() == typeof(KeyValue<,>))
                {
                    if (propInfo.Name == "Key")
                    {
                        return (obj) =>
                        {
                            return ((IKeyValue)obj).Key;
                        };
                    }
                    else
                    {
                        return (obj) =>
                        {
                            return ((IKeyValue)obj).Value;
                        };
                    }
                }
 
                // If either of the arguments to MakeGenericMethod is a valuetype, this is going to cause JITting.
                // Only JIT if dynamic code is supported.
                if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType))
                {
#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported.
                    var createGetterGeneric = s_createGetterInternal.MakeGenericMethod(declaringType, propertyType).CreateDelegate<Func<PropertyInfo, Getter>>();
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
                    return createGetterGeneric(propInfo);
                }
                else
                {
                    return propInfo.GetValue;
                }
            }
            else if (memberInfo is FieldInfo fieldInfo)
            {
                return (obj) =>
                {
                    var value = fieldInfo.GetValue(obj);
                    return value;
                };
            }
            else
            {
                throw new InvalidOperationException(SR.Format(SR.InvalidMember, DataContract.GetClrTypeFullName(memberInfo.DeclaringType!), memberInfo.Name));
            }
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
            Justification = "The call to MakeGenericMethod is safe due to the fact that FastInvokerBuilder.CreateSetterInternal<T, T1> is not annotated.")]
        public static Setter CreateSetter(MemberInfo memberInfo)
        {
            if (memberInfo is PropertyInfo propInfo)
            {
                if (propInfo.CanWrite)
                {
                    Type declaringType = propInfo.DeclaringType!;
                    Type propertyType = propInfo.PropertyType!;
 
                    if (declaringType.IsGenericType && declaringType.GetGenericTypeDefinition() == typeof(KeyValue<,>))
                    {
                        if (propInfo.Name == "Key")
                        {
                            return (ref object obj, object? val) =>
                            {
                                ((IKeyValue)obj).Key = val;
                            };
                        }
                        else
                        {
                            return (ref object obj, object? val) =>
                            {
                                ((IKeyValue)obj).Value = val;
                            };
                        }
                    }
 
                    // If either of the arguments to MakeGenericMethod is a valuetype, this is going to cause JITting.
                    // Only JIT if dynamic code is supported.
                    if (RuntimeFeature.IsDynamicCodeSupported || (!declaringType.IsValueType && !propertyType.IsValueType))
                    {
#pragma warning disable IL3050 // AOT compiling should recognize that this call is gated by RuntimeFeature.IsDynamicCodeSupported.
                        var createSetterGeneric = s_createSetterInternal.MakeGenericMethod(propInfo.DeclaringType!, propInfo.PropertyType).CreateDelegate<Func<PropertyInfo, Setter>>();
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
                        return createSetterGeneric(propInfo);
                    }
                    else
                    {
                        return (ref object obj, object? val) =>
                        {
                            propInfo.SetValue(obj, val);
                        };
                    }
                }
                else
                {
                    throw new InvalidOperationException(SR.Format(SR.NoSetMethodForProperty, propInfo.DeclaringType, propInfo.Name));
                }
            }
            else if (memberInfo is FieldInfo fieldInfo)
            {
                return (ref object obj, object? val) =>
                {
                    fieldInfo.SetValue(obj, val);
                };
            }
            else
            {
                throw new InvalidOperationException(SR.Format(SR.InvalidMember, DataContract.GetClrTypeFullName(memberInfo.DeclaringType!), memberInfo.Name));
            }
        }
 
        private static object Make<T>() where T : new()
        {
            var t = new T();
            return t;
        }
 
        private static Getter CreateGetterInternal<DeclaringType, PropertyType>(PropertyInfo propInfo)
        {
            if (typeof(DeclaringType).IsValueType)
            {
                var getMethod = propInfo.GetMethod!.CreateDelegate<StructGetDelegate<DeclaringType, PropertyType>>();
 
                return (obj) =>
                {
                    var unboxed = (DeclaringType)obj;
                    return getMethod(ref unboxed);
                };
            }
            else
            {
                var getMethod = propInfo.GetMethod!.CreateDelegate<Func<DeclaringType, PropertyType>>();
 
                return (obj) =>
                {
                    return getMethod((DeclaringType)obj);
                };
            }
        }
 
        private static Setter CreateSetterInternal<DeclaringType, PropertyType>(PropertyInfo propInfo)
        {
            if (typeof(DeclaringType).IsValueType)
            {
                var setMethod = propInfo.SetMethod!.CreateDelegate<StructSetDelegate<DeclaringType, PropertyType>>();
 
                return (ref object obj, object? val) =>
                {
                    var unboxed = (DeclaringType)obj;
                    setMethod(ref unboxed, (PropertyType)val!);
                    obj = unboxed!;
                };
            }
            else
            {
                var setMethod = propInfo.SetMethod!.CreateDelegate<Action<DeclaringType, PropertyType>>();
 
                return (ref object obj, object? val) =>
                {
                    setMethod((DeclaringType)obj, (PropertyType)val!);
                };
            }
        }
    }
}