// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Threading.Tasks; using MessagePack.Formatters; using MessagePack.Internal; #pragma warning disable SA1403 // File may only contain a single namespace namespace MessagePack.Resolvers { public sealed class DynamicGenericResolver : IFormatterResolver { /// /// The singleton instance that can be used. /// public static readonly DynamicGenericResolver Instance = new DynamicGenericResolver(); private DynamicGenericResolver() { } public IMessagePackFormatter GetFormatter() { return FormatterCache.Formatter; } private static class FormatterCache { public static readonly IMessagePackFormatter Formatter; static FormatterCache() { Formatter = (IMessagePackFormatter)DynamicGenericResolverGetFormatterHelper.GetFormatter(typeof(T)); } } } } namespace MessagePack.Internal { internal static class DynamicGenericResolverGetFormatterHelper { private static readonly Dictionary FormatterMap = new Dictionary() { { typeof(List<>), typeof(ListFormatter<>) }, { typeof(LinkedList<>), typeof(LinkedListFormatter<>) }, { typeof(Queue<>), typeof(QueueFormatter<>) }, { typeof(Stack<>), typeof(StackFormatter<>) }, { typeof(HashSet<>), typeof(HashSetFormatter<>) }, { typeof(ReadOnlyCollection<>), typeof(ReadOnlyCollectionFormatter<>) }, { typeof(IList<>), typeof(InterfaceListFormatter2<>) }, { typeof(ICollection<>), typeof(InterfaceCollectionFormatter2<>) }, { typeof(IEnumerable<>), typeof(InterfaceEnumerableFormatter<>) }, { typeof(Dictionary<,>), typeof(DictionaryFormatter<,>) }, { typeof(IDictionary<,>), typeof(InterfaceDictionaryFormatter<,>) }, { typeof(SortedDictionary<,>), typeof(SortedDictionaryFormatter<,>) }, { typeof(SortedList<,>), typeof(SortedListFormatter<,>) }, { typeof(ILookup<,>), typeof(InterfaceLookupFormatter<,>) }, { typeof(IGrouping<,>), typeof(InterfaceGroupingFormatter<,>) }, { typeof(ObservableCollection<>), typeof(ObservableCollectionFormatter<>) }, { typeof(ReadOnlyObservableCollection<>), typeof(ReadOnlyObservableCollectionFormatter<>) }, { typeof(IReadOnlyList<>), typeof(InterfaceReadOnlyListFormatter<>) }, { typeof(IReadOnlyCollection<>), typeof(InterfaceReadOnlyCollectionFormatter<>) }, { typeof(ISet<>), typeof(InterfaceSetFormatter<>) }, #if NET5_0_OR_GREATER { typeof(IReadOnlySet<>), typeof(InterfaceReadOnlySetFormatter<>) }, #endif { typeof(System.Collections.Concurrent.ConcurrentBag<>), typeof(ConcurrentBagFormatter<>) }, { typeof(System.Collections.Concurrent.ConcurrentQueue<>), typeof(ConcurrentQueueFormatter<>) }, { typeof(System.Collections.Concurrent.ConcurrentStack<>), typeof(ConcurrentStackFormatter<>) }, { typeof(ReadOnlyDictionary<,>), typeof(ReadOnlyDictionaryFormatter<,>) }, { typeof(IReadOnlyDictionary<,>), typeof(InterfaceReadOnlyDictionaryFormatter<,>) }, { typeof(System.Collections.Concurrent.ConcurrentDictionary<,>), typeof(ConcurrentDictionaryFormatter<,>) }, { typeof(Lazy<>), typeof(LazyFormatter<>) }, }; // Reduce IL2CPP code generate size(don't write long code in ) internal static object GetFormatter(Type t) { TypeInfo ti = t.GetTypeInfo(); if (t.IsArray) { var rank = t.GetArrayRank(); if (rank == 1) { if (t.GetElementType() == typeof(byte)) { // byte[] is also supported in builtin formatter. return ByteArrayFormatter.Instance; } return Activator.CreateInstance(typeof(ArrayFormatter<>).MakeGenericType(t.GetElementType())); } else if (rank == 2) { return Activator.CreateInstance(typeof(TwoDimensionalArrayFormatter<>).MakeGenericType(t.GetElementType())); } else if (rank == 3) { return Activator.CreateInstance(typeof(ThreeDimensionalArrayFormatter<>).MakeGenericType(t.GetElementType())); } else if (rank == 4) { return Activator.CreateInstance(typeof(FourDimensionalArrayFormatter<>).MakeGenericType(t.GetElementType())); } else { return null; // not supported built-in } } else if (ti.IsGenericType) { Type genericType = ti.GetGenericTypeDefinition(); TypeInfo genericTypeInfo = genericType.GetTypeInfo(); var isNullable = genericTypeInfo.IsNullable(); Type nullableElementType = isNullable ? ti.GenericTypeArguments[0] : null; if (genericType == typeof(KeyValuePair<,>)) { return CreateInstance(typeof(KeyValuePairFormatter<,>), ti.GenericTypeArguments); } // Tuple else if (ti.FullName.StartsWith("System.Tuple")) { Type tupleFormatterType = null; switch (ti.GenericTypeArguments.Length) { case 1: tupleFormatterType = typeof(TupleFormatter<>); break; case 2: tupleFormatterType = typeof(TupleFormatter<,>); break; case 3: tupleFormatterType = typeof(TupleFormatter<,,>); break; case 4: tupleFormatterType = typeof(TupleFormatter<,,,>); break; case 5: tupleFormatterType = typeof(TupleFormatter<,,,,>); break; case 6: tupleFormatterType = typeof(TupleFormatter<,,,,,>); break; case 7: tupleFormatterType = typeof(TupleFormatter<,,,,,,>); break; case 8: tupleFormatterType = typeof(TupleFormatter<,,,,,,,>); break; default: break; } return CreateInstance(tupleFormatterType, ti.GenericTypeArguments); } // ValueTuple else if (ti.FullName.StartsWith("System.ValueTuple")) { Type tupleFormatterType = null; switch (ti.GenericTypeArguments.Length) { case 1: tupleFormatterType = typeof(ValueTupleFormatter<>); break; case 2: tupleFormatterType = typeof(ValueTupleFormatter<,>); break; case 3: tupleFormatterType = typeof(ValueTupleFormatter<,,>); break; case 4: tupleFormatterType = typeof(ValueTupleFormatter<,,,>); break; case 5: tupleFormatterType = typeof(ValueTupleFormatter<,,,,>); break; case 6: tupleFormatterType = typeof(ValueTupleFormatter<,,,,,>); break; case 7: tupleFormatterType = typeof(ValueTupleFormatter<,,,,,,>); break; case 8: tupleFormatterType = typeof(ValueTupleFormatter<,,,,,,,>); break; default: break; } return CreateInstance(tupleFormatterType, ti.GenericTypeArguments); } // ArraySegment else if (genericType == typeof(ArraySegment<>)) { if (ti.GenericTypeArguments[0] == typeof(byte)) { return ByteArraySegmentFormatter.Instance; } else { return CreateInstance(typeof(ArraySegmentFormatter<>), ti.GenericTypeArguments); } } // Memory else if (genericType == typeof(Memory<>)) { if (ti.GenericTypeArguments[0] == typeof(byte)) { return ByteMemoryFormatter.Instance; } else { return CreateInstance(typeof(MemoryFormatter<>), ti.GenericTypeArguments); } } // ReadOnlyMemory else if (genericType == typeof(ReadOnlyMemory<>)) { if (ti.GenericTypeArguments[0] == typeof(byte)) { return ByteReadOnlyMemoryFormatter.Instance; } else { return CreateInstance(typeof(ReadOnlyMemoryFormatter<>), ti.GenericTypeArguments); } } // ReadOnlySequence else if (genericType == typeof(ReadOnlySequence<>)) { if (ti.GenericTypeArguments[0] == typeof(byte)) { return ByteReadOnlySequenceFormatter.Instance; } else { return CreateInstance(typeof(ReadOnlySequenceFormatter<>), ti.GenericTypeArguments); } } // Standard Nullable else if (isNullable) { return CreateInstance(typeof(NullableFormatter<>), new[] { nullableElementType }); } // Mapped formatter else { Type formatterType; if (FormatterMap.TryGetValue(genericType, out formatterType)) { return CreateInstance(formatterType, ti.GenericTypeArguments); } } } else if (ti.IsEnum) { return CreateInstance(typeof(GenericEnumFormatter<>), new[] { t }); } else { // NonGeneric Collection if (t == typeof(IEnumerable)) { return NonGenericInterfaceEnumerableFormatter.Instance; } else if (t == typeof(ICollection)) { return NonGenericInterfaceCollectionFormatter.Instance; } else if (t == typeof(IList)) { return NonGenericInterfaceListFormatter.Instance; } else if (t == typeof(IDictionary)) { return NonGenericInterfaceDictionaryFormatter.Instance; } if (typeof(IList).GetTypeInfo().IsAssignableFrom(ti) && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) { return Activator.CreateInstance(typeof(NonGenericListFormatter<>).MakeGenericType(t)); } else if (typeof(IDictionary).GetTypeInfo().IsAssignableFrom(ti) && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) { return Activator.CreateInstance(typeof(NonGenericDictionaryFormatter<>).MakeGenericType(t)); } } // check inherited types(e.g. Foo : ICollection<>, Bar : ICollection) { // generic dictionary var dictionaryDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)); if (dictionaryDef != null && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) { Type keyType = dictionaryDef.GenericTypeArguments[0]; Type valueType = dictionaryDef.GenericTypeArguments[1]; return CreateInstance(typeof(GenericDictionaryFormatter<,,>), new[] { keyType, valueType, t }); } // generic dictionary with collection ctor var dictionaryInterfaceDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && (x.GetGenericTypeDefinition() == typeof(IDictionary<,>) || x.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))); if (dictionaryInterfaceDef != null) { Type keyType = dictionaryInterfaceDef.GenericTypeArguments[0]; Type valueType = dictionaryInterfaceDef.GenericTypeArguments[1]; Type[] allowedParameterTypes = new Type[] { typeof(IDictionary<,>).MakeGenericType(keyType, valueType), typeof(IReadOnlyDictionary<,>).MakeGenericType(keyType, valueType), typeof(IEnumerable<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType)), }; foreach (var constructor in ti.DeclaredConstructors) { ParameterInfo[] parameters = constructor.GetParameters(); if (parameters.Length == 1 && allowedParameterTypes.Any(allowedType => parameters[0].ParameterType.IsAssignableFrom(allowedType))) { return CreateInstance(typeof(GenericReadOnlyDictionaryFormatter<,,>), new[] { keyType, valueType, t }); } } } // generic collection var collectionDef = ti.ImplementedInterfaces.FirstOrDefault(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(ICollection<>)); if (collectionDef != null && ti.DeclaredConstructors.Any(x => x.GetParameters().Length == 0)) { Type elemType = collectionDef.GenericTypeArguments[0]; return CreateInstance(typeof(GenericCollectionFormatter<,>), new[] { elemType, t }); } // generic IEnumerable collection // looking for combination of IEnumerable and constructor that takes // enumeration of the same type foreach (var enumerableCollectionDef in ti.ImplementedInterfaces.Where(x => x.GetTypeInfo().IsConstructedGenericType() && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { Type elemType = enumerableCollectionDef.GenericTypeArguments[0]; Type paramInterface = typeof(IEnumerable<>).MakeGenericType(elemType); foreach (var constructor in ti.DeclaredConstructors) { var parameters = constructor.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(paramInterface)) { return CreateInstance(typeof(GenericEnumerableFormatter<,>), new[] { elemType, t }); } } } } return null; } private static object CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) { return Activator.CreateInstance(genericType.MakeGenericType(genericTypeArguments), arguments); } } }