| File: parent\Common\ReflectionExtensions.cs | Web Access |
| Project: System.Text.Json.csproj (System.Text.Json) |
// 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.Reflection; using System.Collections.Generic; #if !BUILDING_SOURCE_GENERATOR using System.Diagnostics.CodeAnalysis; #endif namespace System.Text.Json.Reflection { internal static partial class ReflectionExtensions { // Immutable collection types. private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1"; private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1"; private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1"; private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1"; private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1"; private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1"; private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1"; private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1"; private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1"; private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1"; private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2"; private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2"; private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2"; // Immutable collection builder types. private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray"; private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList"; private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack"; private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue"; private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet"; private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet"; private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary"; private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary"; public const string CreateRangeMethodName = "CreateRange"; public static Type? GetCompatibleGenericBaseClass(this Type type, Type? baseType) { if (baseType is null) { return null; } Debug.Assert(baseType.IsGenericType); Debug.Assert(!baseType.IsInterface); Debug.Assert(baseType == baseType.GetGenericTypeDefinition()); Type? baseTypeToCheck = type; while (baseTypeToCheck != null && baseTypeToCheck != typeof(object)) { if (baseTypeToCheck.IsGenericType) { Type genericTypeToCheck = baseTypeToCheck.GetGenericTypeDefinition(); if (genericTypeToCheck == baseType) { return baseTypeToCheck; } } baseTypeToCheck = baseTypeToCheck.BaseType; } return null; } #if !BUILDING_SOURCE_GENERATOR [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", Justification = "The 'interfaceType' must exist and so trimmer kept it. In which case " + "It also kept it on any type which implements it. The below call to GetInterfaces " + "may return fewer results when trimmed but it will return the 'interfaceType' " + "if the type implemented it, even after trimming.")] #endif public static Type? GetCompatibleGenericInterface(this Type type, Type? interfaceType) { if (interfaceType is null) { return null; } Debug.Assert(interfaceType.IsGenericType); Debug.Assert(interfaceType.IsInterface); Debug.Assert(interfaceType == interfaceType.GetGenericTypeDefinition()); Type interfaceToCheck = type; if (interfaceToCheck.IsGenericType) { interfaceToCheck = interfaceToCheck.GetGenericTypeDefinition(); } if (interfaceToCheck == interfaceType) { return type; } foreach (Type typeToCheck in type.GetInterfaces()) { if (typeToCheck.IsGenericType) { Type genericInterfaceToCheck = typeToCheck.GetGenericTypeDefinition(); if (genericInterfaceToCheck == interfaceType) { return typeToCheck; } } } return null; } public static bool IsImmutableDictionaryType(this Type type) { if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringComparison.Ordinal)) { return false; } switch (GetBaseNameFromGenericType(type)) { case ImmutableDictionaryGenericTypeName: case ImmutableDictionaryGenericInterfaceTypeName: case ImmutableSortedDictionaryGenericTypeName: return true; default: return false; } } public static bool IsImmutableEnumerableType(this Type type) { if (!type.IsGenericType || !type.Assembly.FullName!.StartsWith("System.Collections.Immutable", StringComparison.Ordinal)) { return false; } switch (GetBaseNameFromGenericType(type)) { case ImmutableArrayGenericTypeName: case ImmutableListGenericTypeName: case ImmutableListGenericInterfaceTypeName: case ImmutableStackGenericTypeName: case ImmutableStackGenericInterfaceTypeName: case ImmutableQueueGenericTypeName: case ImmutableQueueGenericInterfaceTypeName: case ImmutableSortedSetGenericTypeName: case ImmutableHashSetGenericTypeName: case ImmutableSetGenericInterfaceTypeName: return true; default: return false; } } public static string? GetImmutableDictionaryConstructingTypeName(this Type type) { Debug.Assert(type.IsImmutableDictionaryType()); // Use the generic type definition of the immutable collection to determine // an appropriate constructing type, i.e. a type that we can invoke the // `CreateRange<T>` method on, which returns the desired immutable collection. switch (GetBaseNameFromGenericType(type)) { case ImmutableDictionaryGenericTypeName: case ImmutableDictionaryGenericInterfaceTypeName: return ImmutableDictionaryTypeName; case ImmutableSortedDictionaryGenericTypeName: return ImmutableSortedDictionaryTypeName; default: // We verified that the type is an immutable collection, so the // generic definition is one of the above. return null; } } public static string? GetImmutableEnumerableConstructingTypeName(this Type type) { Debug.Assert(type.IsImmutableEnumerableType()); // Use the generic type definition of the immutable collection to determine // an appropriate constructing type, i.e. a type that we can invoke the // `CreateRange<T>` method on, which returns the desired immutable collection. switch (GetBaseNameFromGenericType(type)) { case ImmutableArrayGenericTypeName: return ImmutableArrayTypeName; case ImmutableListGenericTypeName: case ImmutableListGenericInterfaceTypeName: return ImmutableListTypeName; case ImmutableStackGenericTypeName: case ImmutableStackGenericInterfaceTypeName: return ImmutableStackTypeName; case ImmutableQueueGenericTypeName: case ImmutableQueueGenericInterfaceTypeName: return ImmutableQueueTypeName; case ImmutableSortedSetGenericTypeName: return ImmutableSortedSetTypeName; case ImmutableHashSetGenericTypeName: case ImmutableSetGenericInterfaceTypeName: return ImmutableHashSetTypeName; default: // We verified that the type is an immutable collection, so the // generic definition is one of the above. return null; } } private static string GetBaseNameFromGenericType(Type genericType) { Type genericTypeDef = genericType.GetGenericTypeDefinition(); return genericTypeDef.FullName!; } public static bool IsVirtual(this PropertyInfo propertyInfo) { return propertyInfo.GetMethod?.IsVirtual == true || propertyInfo.SetMethod?.IsVirtual == true; } public static bool IsKeyValuePair(this Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>); public static bool TryGetDeserializationConstructor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] this Type type, bool useDefaultCtorInAnnotatedStructs, out ConstructorInfo? deserializationCtor) { ConstructorInfo? ctorWithAttribute = null; ConstructorInfo? publicParameterlessCtor = null; ConstructorInfo? lonePublicCtor = null; ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); if (constructors.Length == 1) { lonePublicCtor = constructors[0]; } foreach (ConstructorInfo constructor in constructors) { if (HasJsonConstructorAttribute(constructor)) { if (ctorWithAttribute != null) { deserializationCtor = null; return false; } ctorWithAttribute = constructor; } else if (constructor.GetParameters().Length == 0) { publicParameterlessCtor = constructor; } } // Search for non-public ctors with [JsonConstructor]. foreach (ConstructorInfo constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)) { if (HasJsonConstructorAttribute(constructor)) { if (ctorWithAttribute != null) { deserializationCtor = null; return false; } ctorWithAttribute = constructor; } } // Structs will use default constructor if attribute isn't used. if (useDefaultCtorInAnnotatedStructs && type.IsValueType && ctorWithAttribute == null) { deserializationCtor = null; return true; } deserializationCtor = ctorWithAttribute ?? publicParameterlessCtor ?? lonePublicCtor; return true; } public static object? GetDefaultValue(this ParameterInfo parameterInfo) { Type parameterType = parameterInfo.ParameterType; object? defaultValue = parameterInfo.DefaultValue; if (defaultValue is null) { return null; } // DBNull.Value is sometimes used as the default value (returned by reflection) of nullable params in place of null. if (defaultValue == DBNull.Value && parameterType != typeof(DBNull)) { return null; } // Default values of enums or nullable enums are represented using the underlying type and need to be cast explicitly // cf. https://github.com/dotnet/runtime/issues/68647 if (parameterType.IsEnum) { return Enum.ToObject(parameterType, defaultValue); } if (Nullable.GetUnderlyingType(parameterType) is Type underlyingType && underlyingType.IsEnum) { return Enum.ToObject(underlyingType, defaultValue); } return defaultValue; } /// <summary> /// Returns the type hierarchy for the given type, starting from the current type up to the base type(s) in the hierarchy. /// Interface hierarchies with multiple inheritance will return results using topological sorting. /// </summary> [RequiresUnreferencedCode("Should only be used by the reflection-based serializer.")] public static Type[] GetSortedTypeHierarchy(this Type type) { if (!type.IsInterface) { // Non-interface hierarchies are linear, just walk up to the earliest ancestor. var results = new List<Type>(); for (Type? current = type; current != null; current = current.BaseType) { results.Add(current); } return results.ToArray(); } else { // Interface hierarchies support multiple inheritance. // For consistency with class hierarchy resolution order, // sort topologically from most derived to least derived. return JsonHelpers.TraverseGraphWithTopologicalSort(type, static t => t.GetInterfaces()); } } } }