// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json.Reflection; namespace System.Text.Json.Serialization { internal static class IEnumerableConverterFactoryHelpers { // System.Text.Json doesn't take a direct reference to System.Collections.Immutable so // any netstandard2.0 consumers don't need to reference System.Collections.Immutable. // So instead, implement a "weak reference" by using strings to check for Immutable types. // Don't use DynamicDependency attributes to the Immutable Collection types so they can be trimmed in applications that don't use Immutable Collections. internal const string ImmutableConvertersUnreferencedCodeMessage = "System.Collections.Immutable converters use Reflection to find and create Immutable Collection types, which requires unreferenced code."; [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] [RequiresDynamicCode(ImmutableConvertersUnreferencedCodeMessage)] public static MethodInfo GetImmutableEnumerableCreateRangeMethod(this Type type, Type elementType) { Type? constructingType = GetImmutableEnumerableConstructingType(type); if (constructingType != null) { MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); foreach (MethodInfo method in constructingTypeMethods) { if (method.Name == ReflectionExtensions.CreateRangeMethodName && method.GetParameters().Length == 1 && method.IsGenericMethod && method.GetGenericArguments().Length == 1) { return method.MakeGenericMethod(elementType); } } } ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(type); return null!; } [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] [RequiresDynamicCode(ImmutableConvertersUnreferencedCodeMessage)] public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type, Type keyType, Type valueType) { Type? constructingType = GetImmutableDictionaryConstructingType(type); if (constructingType != null) { MethodInfo[] constructingTypeMethods = constructingType.GetMethods(); foreach (MethodInfo method in constructingTypeMethods) { if (method.Name == ReflectionExtensions.CreateRangeMethodName && method.GetParameters().Length == 1 && method.IsGenericMethod && method.GetGenericArguments().Length == 2) { return method.MakeGenericMethod(keyType, valueType); } } } ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(type); return null!; } [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] [RequiresDynamicCode(ImmutableConvertersUnreferencedCodeMessage)] private static Type? GetImmutableEnumerableConstructingType(Type type) { Debug.Assert(type.IsImmutableEnumerableType()); string? constructingTypeName = type.GetImmutableEnumerableConstructingTypeName(); return constructingTypeName == null ? null : type.Assembly.GetType(constructingTypeName); } [RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)] [RequiresDynamicCode(ImmutableConvertersUnreferencedCodeMessage)] private static Type? GetImmutableDictionaryConstructingType(Type type) { Debug.Assert(type.IsImmutableDictionaryType()); string? constructingTypeName = type.GetImmutableDictionaryConstructingTypeName(); return constructingTypeName == null ? null : type.Assembly.GetType(constructingTypeName); } public static bool IsNonGenericStackOrQueue(this Type type) { #if NET // Optimize for linking scenarios where mscorlib is trimmed out. const string stackTypeName = "System.Collections.Stack, System.Collections.NonGeneric"; const string queueTypeName = "System.Collections.Queue, System.Collections.NonGeneric"; #else const string stackTypeName = "System.Collections.Stack, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string queueTypeName = "System.Collections.Queue, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; #endif Type? stackType = GetTypeIfExists(stackTypeName); if (stackType?.IsAssignableFrom(type) == true) { return true; } Type? queueType = GetTypeIfExists(queueTypeName); if (queueType?.IsAssignableFrom(type) == true) { return true; } return false; } // This method takes an unannotated string which makes trimming reflection analysis lose track of the type we are // looking for. This indirection allows the removal of the type if it is not used in the calling application. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", Justification = "This method exists to allow for 'weak references' to the Stack and Queue types. If those types are used in the app, " + "they will be preserved by the app and Type.GetType will return them. If those types are not used in the app, we don't want to preserve them here.")] private static Type? GetTypeIfExists(string name) => Type.GetType(name, false); } } |