< Summary

Information
Class: System.Text.Json.Serialization.Metadata.FSharpCoreReflectionProxy
Assembly: System.Text.Json
File(s): C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\FSharpCoreReflectionProxy.cs
Line coverage
13%
Covered lines: 14
Uncovered lines: 90
Coverable lines: 104
Total lines: 261
Line coverage: 13.4%
Branch coverage
12%
Covered branches: 5
Total branches: 40
Branch coverage: 12.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\FSharpCoreReflectionProxy.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;
 8
 9namespace System.Text.Json.Serialization.Metadata
 10{
 11    // Recognizing types emitted by the F# compiler requires consuming APIs from the FSharp.Core runtime library.
 12    // Every F# application ships with a copy of FSharp.Core, however it is not available statically to System.Text.Json
 13    // The following class uses reflection to access the relevant APIs required to detect the various F# types we are lo
 14
 15    /// <summary>
 16    /// Proxy class used to access FSharp.Core metadata and reflection APIs that are not statically available to System.
 17    /// </summary>
 18    internal sealed class FSharpCoreReflectionProxy
 19    {
 20        /// <summary>
 21        /// The various categories of F# types that System.Text.Json supports.
 22        /// </summary>
 23        public enum FSharpKind
 24        {
 25            Unrecognized,
 26            Option,
 27            ValueOption,
 28            List,
 29            Set,
 30            Map,
 31            Record,
 32            Union
 33        }
 34
 35        // Binding a struct getter method to a delegate requires that the struct parameter is passed byref.
 36        public delegate TResult StructGetter<TStruct, TResult>(ref TStruct @this) where TStruct : struct;
 37
 38        public const string FSharpCoreUnreferencedCodeMessage = "Uses Reflection to access FSharp.Core components at run
 39
 40        private static FSharpCoreReflectionProxy? s_singletonInstance;
 41
 42        // Every type generated by the F# compiler is annotated with the CompilationMappingAttribute
 43        // containing all relevant metadata required to determine its kind:
 44        // https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-compilationmappingattribute.html#SourceConstr
 45        private const string CompilationMappingAttributeTypeName = "Microsoft.FSharp.Core.CompilationMappingAttribute";
 46        private readonly Type _compilationMappingAttributeType;
 47        private readonly MethodInfo? _sourceConstructFlagsGetter;
 48
 49        private readonly Type? _fsharpOptionType;
 50        private readonly Type? _fsharpValueOptionType;
 51        private readonly Type? _fsharpListType;
 52        private readonly Type? _fsharpSetType;
 53        private readonly Type? _fsharpMapType;
 54
 55        private readonly MethodInfo? _fsharpListCtor;
 56        private readonly MethodInfo? _fsharpSetCtor;
 57        private readonly MethodInfo? _fsharpMapCtor;
 58
 59        /// <summary>
 60        /// Checks if the provided System.Type instance is emitted by the F# compiler.
 61        /// If true, also initializes the proxy singleton for future by other F# types.
 62        /// </summary>
 63        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 64        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 65        public static bool IsFSharpType(Type type)
 230166        {
 230167            if (s_singletonInstance is null)
 230168            {
 230169                if (GetFSharpCoreAssembly(type) is Assembly fsharpCoreAssembly)
 070                {
 71                    // Type is F# type, initialize the singleton instance.
 072                    s_singletonInstance ??= new FSharpCoreReflectionProxy(fsharpCoreAssembly);
 73
 074                    return true;
 75                }
 76
 230177                return false;
 78            }
 79
 080            return s_singletonInstance.GetFSharpCompilationMappingAttribute(type) is not null;
 230181        }
 82
 83        /// <summary>
 84        /// Gets the singleton proxy instance; prerequires a successful IsFSharpType call for proxy initialization.
 85        /// </summary>
 86        public static FSharpCoreReflectionProxy Instance
 87        {
 88            get
 089            {
 090                Debug.Assert(s_singletonInstance is not null, "should be initialized via a successful IsFSharpType call.
 091                return s_singletonInstance;
 092            }
 93        }
 94
 95        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 96        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 097        private FSharpCoreReflectionProxy(Assembly fsharpCoreAssembly)
 098        {
 099            Debug.Assert(fsharpCoreAssembly.GetName().Name == "FSharp.Core");
 100
 0101            Type compilationMappingAttributeType = fsharpCoreAssembly.GetType(CompilationMappingAttributeTypeName)!;
 0102            _sourceConstructFlagsGetter = compilationMappingAttributeType.GetMethod("get_SourceConstructFlags", BindingF
 0103            _compilationMappingAttributeType = compilationMappingAttributeType;
 104
 0105            _fsharpOptionType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Core.FSharpOption`1");
 0106            _fsharpValueOptionType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Core.FSharpValueOption`1");
 0107            _fsharpListType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpList`1");
 0108            _fsharpSetType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpSet`1");
 0109            _fsharpMapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2");
 110
 0111            _fsharpListCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.ListModule")?.GetMethod("OfSeq", 
 0112            _fsharpSetCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.SetModule")?.GetMethod("OfSeq", Bi
 0113            _fsharpMapCtor = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.MapModule")?.GetMethod("OfSeq", Bi
 0114        }
 115
 116        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 117        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 118        public FSharpKind DetectFSharpKind(Type type)
 0119        {
 0120            Attribute? compilationMappingAttribute = GetFSharpCompilationMappingAttribute(type);
 121
 0122            if (compilationMappingAttribute is null)
 0123            {
 0124                return FSharpKind.Unrecognized;
 125            }
 126
 0127            if (type.IsGenericType)
 0128            {
 0129                Type genericType = type.GetGenericTypeDefinition();
 0130                if (genericType == _fsharpOptionType) return FSharpKind.Option;
 0131                if (genericType == _fsharpValueOptionType) return FSharpKind.ValueOption;
 0132                if (genericType == _fsharpListType) return FSharpKind.List;
 0133                if (genericType == _fsharpSetType) return FSharpKind.Set;
 0134                if (genericType == _fsharpMapType) return FSharpKind.Map;
 0135            }
 136
 0137            return (GetSourceConstructFlags(compilationMappingAttribute) & SourceConstructFlags.KindMask) switch
 0138            {
 0139                SourceConstructFlags.RecordType => FSharpKind.Record,
 0140                SourceConstructFlags.SumType => FSharpKind.Union,
 0141                _ => FSharpKind.Unrecognized
 0142            };
 0143        }
 144
 145        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 146        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 147        public Func<TFSharpOption, T> CreateFSharpOptionValueGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMembe
 0148        {
 0149            Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpOptionType);
 0150            MethodInfo valueGetter = EnsureMemberExists(typeof(TFSharpOption).GetMethod("get_Value", BindingFlags.Public
 0151            return CreateDelegate<Func<TFSharpOption, T>>(valueGetter);
 0152        }
 153
 154        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 155        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 156        public Func<TElement?, TFSharpOption> CreateFSharpOptionSomeConstructor<[DynamicallyAccessedMembers(DynamicallyA
 0157        {
 0158            Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpOptionType);
 0159            MethodInfo methodInfo = EnsureMemberExists(typeof(TFSharpOption).GetMethod("Some", BindingFlags.Public | Bin
 0160            return CreateDelegate<Func<TElement?, TFSharpOption>>(methodInfo);
 0161        }
 162
 163        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 164        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 165        public StructGetter<TFSharpValueOption, TElement> CreateFSharpValueOptionValueGetter<[DynamicallyAccessedMembers
 166            where TFSharpValueOption : struct
 0167        {
 0168            Debug.Assert(typeof(TFSharpValueOption).GetGenericTypeDefinition() == _fsharpValueOptionType);
 0169            MethodInfo valueGetter = EnsureMemberExists(typeof(TFSharpValueOption).GetMethod("get_Value", BindingFlags.P
 0170            return CreateDelegate<StructGetter<TFSharpValueOption, TElement>>(valueGetter);
 0171        }
 172
 173        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 174        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 175        public Func<TElement?, TFSharpOption> CreateFSharpValueOptionSomeConstructor<[DynamicallyAccessedMembers(Dynamic
 0176        {
 0177            Debug.Assert(typeof(TFSharpOption).GetGenericTypeDefinition() == _fsharpValueOptionType);
 0178            MethodInfo methodInfo = EnsureMemberExists(typeof(TFSharpOption).GetMethod("Some", BindingFlags.Public | Bin
 0179            return CreateDelegate<Func<TElement?, TFSharpOption>>(methodInfo);
 0180        }
 181
 182        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 183        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 184        public Func<IEnumerable<TElement>, TFSharpList> CreateFSharpListConstructor<TFSharpList, TElement>()
 0185        {
 0186            Debug.Assert(typeof(TFSharpList).GetGenericTypeDefinition() == _fsharpListType);
 0187            return CreateDelegate<Func<IEnumerable<TElement>, TFSharpList>>(EnsureMemberExists(_fsharpListCtor, "Microso
 0188        }
 189
 190        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 191        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 192        public Func<IEnumerable<TElement>, TFSharpSet> CreateFSharpSetConstructor<TFSharpSet, TElement>()
 0193        {
 0194            Debug.Assert(typeof(TFSharpSet).GetGenericTypeDefinition() == _fsharpSetType);
 0195            return CreateDelegate<Func<IEnumerable<TElement>, TFSharpSet>>(EnsureMemberExists(_fsharpSetCtor, "Microsoft
 0196        }
 197
 198        [RequiresUnreferencedCode(FSharpCoreUnreferencedCodeMessage)]
 199        [RequiresDynamicCode(FSharpCoreUnreferencedCodeMessage)]
 200        public Func<IEnumerable<Tuple<TKey, TValue>>, TFSharpMap> CreateFSharpMapConstructor<TFSharpMap, TKey, TValue>()
 0201        {
 0202            Debug.Assert(typeof(TFSharpMap).GetGenericTypeDefinition() == _fsharpMapType);
 0203            return CreateDelegate<Func<IEnumerable<Tuple<TKey, TValue>>, TFSharpMap>>(EnsureMemberExists(_fsharpMapCtor,
 0204        }
 205
 206        private Attribute? GetFSharpCompilationMappingAttribute(Type type)
 0207        {
 0208            object[] attributes = type.GetCustomAttributes(_compilationMappingAttributeType, inherit: true);
 0209            return attributes.Length == 0 ? null : (Attribute)attributes[0];
 0210        }
 211
 212        private SourceConstructFlags GetSourceConstructFlags(Attribute compilationMappingAttribute)
 0213            => _sourceConstructFlagsGetter is null ? SourceConstructFlags.None : (SourceConstructFlags)_sourceConstructF
 214
 215        // If the provided type is generated by the F# compiler, returns the runtime FSharp.Core assembly.
 216        private static Assembly? GetFSharpCoreAssembly(Type type)
 2301217        {
 20931218            foreach (Attribute attr in type.GetCustomAttributes(inherit: true))
 7014219            {
 7014220                Type attributeType = attr.GetType();
 7014221                if (attributeType.FullName == CompilationMappingAttributeTypeName)
 0222                {
 0223                    return attributeType.Assembly;
 224                }
 7014225            }
 226
 2301227            return null;
 2301228        }
 229
 230        private static TDelegate CreateDelegate<TDelegate>(MethodInfo methodInfo) where TDelegate : Delegate
 0231            => (TDelegate)Delegate.CreateDelegate(typeof(TDelegate), methodInfo, throwOnBindFailure: true)!;
 232
 233        private static TMemberInfo EnsureMemberExists<TMemberInfo>(TMemberInfo? memberInfo, string memberName) where TMe
 0234        {
 0235            if (memberInfo is null)
 0236            {
 0237                ThrowHelper.ThrowMissingMemberException_MissingFSharpCoreMember(memberName);
 238            }
 239
 0240            return memberInfo;
 0241        }
 242
 243        // Replicates the F# source construct flags enum
 244        // https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-sourceconstructflags.html
 245        private enum SourceConstructFlags
 246        {
 247            None = 0,
 248            SumType = 1,
 249            RecordType = 2,
 250            ObjectType = 3,
 251            Field = 4,
 252            Exception = 5,
 253            Closure = 6,
 254            Module = 7,
 255            UnionCase = 8,
 256            Value = 9,
 257            KindMask = 31,
 258            NonPublicRepresentation = 32
 259        }
 260    }
 261}