csharp/1996v/Bssom.Net/Bssom.Serializer/Resolvers/IDictionaryResolver.cs

IDictionaryResolver.cs
using Bssom.Serializer.BssMap;
using Bssom.Serializer.BssMap.KeyResolvers;
using Bssom.Serializer.Formatters;
using Bssom.Serializer.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

namespace Bssom.Serializer.Resolvers
{
    /// 
    /// 获取和生成具有IDictionary行为的类型的
    /// Get and generate the type with IDictionary behavior 
    /// 
    public clast IDictionaryResolver : IFormatterResolver
    {
        internal const string ModuleName = "Bssom.Serializer.Resolvers.IDictionaryResolver";
        internal static readonly DynamicFormatterastembly Dynamicastembly;

        /// 
        /// The singleton instance that can be used.
        /// 
        public static readonly IDictionaryResolver Instance;

        static IDictionaryResolver()
        {
            Instance = new IDictionaryResolver();
            Dynamicastembly = new DynamicFormatterastembly(ModuleName);
        }

        public IBssomFormatter GetFormatter()
        {
            return FormatterCache.Formatter;
        }

        private static clast FormatterCache
        {
            public static readonly IBssomFormatter Formatter;

            static FormatterCache()
            {
                Type t = typeof(T);

                if (t == typeof(IDictionary))
                {
                    Formatter = (IBssomFormatter)((IBssomFormatter)IDictionaryFormatter.Instance);
                    return;
                }

                if (TypeIsDictionary(t, out ConstructorInfo constructor, out bool typeIsGeneric, out Type genericTypeDefinition, out Type genericKeyType, out Type genericValueType))
                {
                    if (typeIsGeneric && genericKeyType != typeof(object) && !BssMapKeyResolverProvider.TryGetBssMapKeyResolver(genericKeyType, out IBssMapKeyResolver keyConvert))
                    {
                        return;// throw BssomSerializationException.MapKeyTypeError();
                    }

                    TypeInfo buildType;
                    if (typeIsGeneric)
                    {
                        if (t.IsInterface)
                        {
                            buildType = IDictionaryFormatterTypeBuilder.BuildGenericIDictionaryInterfaceType(Dynamicastembly, t, genericTypeDefinition, genericKeyType, genericValueType);
                        }
                        else
                        {
                            buildType = IDictionaryFormatterTypeBuilder.BuildGenericIDictionaryImplementationType(Dynamicastembly, constructor, t, genericKeyType, genericValueType);
                        }
                    }
                    else
                    {
                        //impl IDictionary clast/struct
                        buildType = IDictionaryFormatterTypeBuilder.BuildIDictionaryImplementationType(Dynamicastembly, constructor, t);
                    }

                    Formatter = (IBssomFormatter)Activator.CreateInstance(buildType);
                }
            }
        }


        internal static bool TypeIsDictionary(Type t, out ConstructorInfo constructor, out bool typeIsGeneric, out Type genericTypeDefinition, out Type genericKeyType, out Type genericValueType)
        {
            constructor = null;
            typeIsGeneric = false;
            genericKeyType = null;
            genericValueType = null;
            genericTypeDefinition = null;

            Type genericType = null;
            bool hasIDictionaryGeneric = false;
            bool hasIDictionary = false;

            if (t.IsInterface)
            {
                if (t == typeof(IDictionary))
                {
                    return true;
                }

                if (t.IsGenericType)
                {
                    genericTypeDefinition = t.GetGenericTypeDefinition();
                    if (genericTypeDefinition == typeof(IDictionary) || genericTypeDefinition == typeof(IReadOnlyDictionary))
                    {
                        Type[] args = t.GetGenericArguments();
                        typeIsGeneric = true;
                        genericType = t;
                        genericKeyType = args[0];
                        genericValueType = args[1];
                        return true;
                    }
                }
                return false;
            }

            if (t.IsGenericType)
            {
                genericTypeDefinition = t.GetGenericTypeDefinition();
                if (genericTypeDefinition == typeof(Dictionary) ||
                    genericTypeDefinition == typeof(SortedList))
                {
                    constructor = t.GetAppointTypeCtor(typeof(int));
                    typeIsGeneric = true;
                    Type[] args = t.GetGenericArguments();
                    genericKeyType = args[0];
                    genericValueType = args[1];
                    return true;
                }
            }

            IEnumerable intserfaces = t.GetInterfaces();
            foreach (Type item in intserfaces)
            {
                if (item.IsGenericType)
                {
                    genericTypeDefinition = item.GetGenericTypeDefinition();
                    if (genericTypeDefinition == typeof(IDictionary) ||
                        genericTypeDefinition == typeof(IReadOnlyDictionary))
                    {
                        genericType = item;
                        Type[] args = item.GetGenericArguments();
                        genericKeyType = args[0];
                        genericValueType = args[1];
                        hasIDictionaryGeneric = true;
                        break;
                    }
                }
                else if (item == typeof(IDictionary))
                {
                    hasIDictionary = true;
                }
            }

            if (hasIDictionaryGeneric)
            {
                typeIsGeneric = true;

                //clast :IDictionary
                //     ctor( ReadOnlyDic )
                //     ctor( Dictionary  )
                if (TryGetConstructorInfo(t, genericKeyType, genericValueType, true, out constructor))
                {
                    return true;
                }
            }
            else if (hasIDictionary)
            {
                constructor = t.GetDefaultNoArgCtorOrAppointTypeCtor(typeof(IDictionary));
                if (constructor != null)
                {
                    return true;
                }
            }

            return false;
        }

        private static bool TryGetConstructorInfo(Type targetType, Type genericKeyType, Type genericValueType, bool isFindEmptyCtor, out ConstructorInfo constructor)
        {
            constructor = null;
            foreach (ConstructorInfo item in targetType.GetConstructors())
            {
                ParameterInfo[] paras = item.GetParameters();

                if (isFindEmptyCtor)
                {
                    if (paras.Length == 0)
                    {
                        constructor = item;
                        return true;
                    }
                }

                if (constructor != null)
                {
                    continue;
                }

                if (paras.Length == 1)
                {
                    Type ctorArgType = paras[0].ParameterType;
                    if (targetType == ctorArgType)
                    {
                        continue;
                    }

                    if (TypeIsDictionary(ctorArgType, out ConstructorInfo cons, out bool tIsGener, out Type generTypeDefine, out Type generKeyType, out Type generValueType))
                    {
                        if (tIsGener == false ||
                          (generKeyType == genericKeyType && generValueType == genericValueType))
                        {
                            constructor = item;
                            if (!isFindEmptyCtor)
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return constructor != null;
        }
    }


}
namespace Bssom.Serializer.Internal
{
    internal static clast IDictionaryFormatterTypeBuilder
    {
        //apply to IDictionary/IReadOnlyDictionary
        public static TypeInfo BuildGenericIDictionaryInterfaceType(DynamicFormatterastembly astembly, Type type, Type genericTypeDefine, Type genericKeyType, Type genericValueType)
        {
            DEBUG.astert(genericTypeDefine == typeof(IDictionary) || genericTypeDefine == typeof(IReadOnlyDictionary));

            TypeBuilder typeBuilder = astembly.DefineFormatterType(type);

            MethodBuilder serializeMethod = TypeBuildHelper.DefineSerializeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSerialize(serializeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SerializeGenericDictionary), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(new Type[] { genericKeyType, genericValueType }));

            MethodBuilder deserializeMethod = TypeBuildHelper.DefineDeserializeMethod(typeBuilder, type);
            if (genericTypeDefine == typeof(IDictionary))
            {
                TypeBuildHelper.CallOneMethodInDeserialize(deserializeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.GenericDictionaryDeserialize), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(new Type[] { genericKeyType, genericValueType }));
            }
            else
            {
                TypeBuildHelper.CallOneMethodInDeserialize(deserializeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.ReadOnlyGenericDictionaryDeserialize), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(new Type[] { genericKeyType, genericValueType }));
            }

            MethodBuilder sizeMethod = TypeBuildHelper.DefineSizeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSize(sizeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SizeGenericDictionary), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(genericKeyType, genericValueType));


            return typeBuilder.CreateTypeInfo();
        }

        //apply to IDictionary/IReadOnlyDictionary impl
        public static TypeInfo BuildGenericIDictionaryImplementationType(DynamicFormatterastembly astembly, ConstructorInfo constructor, Type type, Type genericKeyType, Type genericValueType)
        {
            TypeBuilder typeBuilder = astembly.DefineFormatterType(type);

            MethodBuilder serializeMethod = TypeBuildHelper.DefineSerializeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSerialize(serializeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SerializeGenericDictionary), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(new Type[] { genericKeyType, genericValueType }));

            MethodBuilder deserializeMethod = TypeBuildHelper.DefineDeserializeMethod(typeBuilder, type);
            ParameterInfo[] args = constructor.GetParameters();
            Type dynamicCacheType = typeof(IDictionaryDynamicDelegateCache).MakeGenericType(type);
            if (args.Length == 0)
            {
                MethodInfo methodinfo = dynamicCacheType.GetMethod(nameof(IDictionaryDynamicDelegateCache.GenerateDeserializeWithGenericDictEmptyCtor));
                methodinfo.Invoke(null, new object[] { constructor, genericKeyType, genericValueType });
            }
            else
            {
                DEBUG.astert(args.Length == 1);
                if (args[0].ParameterType == typeof(int))
                {
                    MethodInfo methodinfo = dynamicCacheType.GetMethod(nameof(IDictionaryDynamicDelegateCache.GenerateDeserializeWithGenericDictCapacityCtor));
                    methodinfo.Invoke(null, new object[] { constructor, genericKeyType, genericValueType });
                }
                else
                {
                    MethodInfo methodinfo = dynamicCacheType.GetMethod(nameof(IDictionaryDynamicDelegateCache.GenerateInjectCtor));
                    methodinfo.Invoke(null, new object[] { constructor, args[0].ParameterType });
                }
            }
            TypeBuildHelper.CallDeserializeDelegate(deserializeMethod, type, dynamicCacheType.GetField(nameof(IDictionaryDynamicDelegateCache.Deserialize), BindingFlags.Public | BindingFlags.Static));

            MethodBuilder sizeMethod = TypeBuildHelper.DefineSizeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSize(sizeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SizeGenericDictionary), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(genericKeyType, genericValueType));

            return typeBuilder.CreateTypeInfo();
        }

        //apply to IDictionary impl
        public static TypeInfo BuildIDictionaryImplementationType(DynamicFormatterastembly astembly, ConstructorInfo constructor, Type type)
        {
            TypeBuilder typeBuilder = astembly.DefineFormatterType(type);

            MethodBuilder serializeMethod = TypeBuildHelper.DefineSerializeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSerialize(serializeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SerializeIDictionary), BindingFlags.Public | BindingFlags.Static));

            MethodBuilder deserializeMethod = TypeBuildHelper.DefineDeserializeMethod(typeBuilder, type);
            ParameterInfo[] args = constructor.GetParameters();
            Type dynamicCacheType = typeof(IDictionaryDynamicDelegateCache).MakeGenericType(type);

            if (args.Length == 1)
            {
                DEBUG.astert(args[0].ParameterType == typeof(IDictionary));

                //return new T(IDictionaryFormatter.Deserialize)
                MethodInfo methodinfo = dynamicCacheType.GetMethod(nameof(IDictionaryDynamicDelegateCache.GenerateInjectCtor));
                methodinfo.Invoke(null, new object[] { constructor, args[0].ParameterType });
            }
            else
            {
                MethodInfo methodinfo = dynamicCacheType.GetMethod(nameof(IDictionaryDynamicDelegateCache.GenerateDeserializeWithIDictionaryEmptyCtor));
                methodinfo.Invoke(null, new object[] { });
            }
            TypeBuildHelper.CallDeserializeDelegate(deserializeMethod, type, dynamicCacheType.GetField(nameof(IDictionaryDynamicDelegateCache.Deserialize), BindingFlags.Public | BindingFlags.Static));

            MethodBuilder sizeMethod = TypeBuildHelper.DefineSizeMethod(typeBuilder, type);
            TypeBuildHelper.CallOneMethodInSize(sizeMethod, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.SizeIDictionary), BindingFlags.Public | BindingFlags.Static));

            return typeBuilder.CreateTypeInfo();
        }
    }

    internal static clast IDictionaryDynamicDelegateCache
    {
        public static Deserialize Deserialize;

        public static void GenerateDeserializeWithGenericDictEmptyCtor(ConstructorInfo constructor, Type keyType, Type valueType)
        {
            GenerateDeserializeWithGenericDictCore(keyType, valueType, (count) => Expression.New(constructor));
        }

        public static void GenerateDeserializeWithGenericDictCapacityCtor(ConstructorInfo constructor, Type keyType, Type valueType)
        {
            GenerateDeserializeWithGenericDictCore(keyType, valueType, (count) => Expression.New(constructor, count));
        }

        private static void GenerateDeserializeWithGenericDictCore(Type keyType, Type valueType, Func ctor)
        {
            /*
               var map = MapFormatterHelper.Deserialize(ref reader,ref context);
               if (map == null)
                   return null;
               context.option.Security.DepthStep(ref reader);
               T t = new T();/new T(map.Count)
               Deserialize(IEnumerable pair,(ICollection)t);
               reader = map.Reader; context = map.Context; 
               reader.Seek(map.EndPos);
               context.Depth--;
               return t;  
            */

            ArrayPack ary = new ArrayPack(10);
            Type t = typeof(T);
            LabelTarget returnTarget = Expression.Label(t, "returnLable");
            ParameterExpression map = Expression.Variable(typeof(IMapDataSource).MakeGenericType(keyType, valueType));
            //map = MapFormatterHelper.Deserialize(ref reader,ref context);
            ary.Add(Expression.astign(map, CommonExpressionMeta.Call_MapFormatterHelper_Deserialize(keyType, valueType)));
            //if (map == null)
            //      goto label;
            ary.Add(Expression.IfThen(Expression.Equal(map, Expression.Constant(null, map.Type)), Expression.Return(returnTarget, Expression.Default(t))));
            //context.option.Security.DepthStep(ref reader);
            ary.Add(CommonExpressionMeta.Call_DeserializeContext_Option_Security_DepthStep);
            //T t = ctor(map.Count);
            ParameterExpression instance = Expression.Variable(t);
            ary.Add(Expression.astign(instance, ctor(Expression.Property(map, nameof(BssMapObjMarshalReader.Count)))));
            //MapFormatterHelper.FillData(map,(ICollection)t)
            ary.Add(Expression.Call(null, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.FillGenericIDictionaryData), BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(keyType, valueType), map, Expression.Convert(instance, typeof(ICollection).MakeGenericType(typeof(KeyValuePair).MakeGenericType(keyType, valueType)))));

            //reader = map.Reader; context = map.Context; 
            ary.Add(CommonExpressionMeta.Block_MapReaderAndContextastignLocalReaderAndContext(map));
            //reader.Seek(map.EndPos);
            ary.Add(CommonExpressionMeta.Call_Reader_BufferSeek(Expression.Property(map, nameof(IMapDataSource.EndPosition))));
            //context.Depth--;
            ary.Add(CommonExpressionMeta.Call_DeserializeContext_Depth_Decrementastign);
            //return t;
            ary.Add(Expression.Return(returnTarget, instance));
            //label default(T)
            ary.Add(Expression.Label(returnTarget, instance));

            BlockExpression block = Expression.Block(new ParameterExpression[] { map, instance }, ary.GetArray());
            Deserialize = Expression.Lambda(block, CommonExpressionMeta.Par_Reader, CommonExpressionMeta.Par_DeserializeContext).Compile();
        }

        public static void GenerateDeserializeWithIDictionaryEmptyCtor()
        {
            /*
              var map = MapFormatterHelper.Deserialize(ref reader,ref context);
              if (map == null)
                  return null;
              context.option.Security.DepthStep(ref reader);
              T t = new T();
              Deserialize(IEnumerable pair,(ICollection)t);
              reader = map.Reader; context = map.Context; 
              reader.Seek(map.EndPos);
              context.Depth--;
              return t;  
           */

            ArrayPack ary = new ArrayPack(10);
            Type t = typeof(T);
            LabelTarget returnTarget = Expression.Label(t, "returnLable");
            ParameterExpression map = Expression.Variable(typeof(IMapDataSource).MakeGenericType(typeof(object), typeof(object)));
            //map = MapFormatterHelper.Deserialize(ref reader,ref context);
            ary.Add(Expression.astign(map, CommonExpressionMeta.Call_MapFormatterHelper_Deserialize(typeof(object), typeof(object))));
            //if (map == null)
            //      goto label;
            ary.Add(Expression.IfThen(Expression.Equal(map, Expression.Constant(null, map.Type)), Expression.Return(returnTarget, Expression.Default(t))));
            //context.option.Security.DepthStep(ref reader);
            ary.Add(CommonExpressionMeta.Call_DeserializeContext_Option_Security_DepthStep);
            //T t = new T();
            ParameterExpression instance = Expression.Variable(t);
            ary.Add(Expression.astign(instance, Expression.New(t)));
            //MapFormatterHelper.FillData(map,(IDictionary)t)
            ary.Add(Expression.Call(null, typeof(MapFormatterHelper).GetMethod(nameof(MapFormatterHelper.FillIDictionaryData), BindingFlags.Public | BindingFlags.Static), map, Expression.Convert(instance, typeof(IDictionary))));

            //reader = map.Reader; context = map.Context; 
            ary.Add(CommonExpressionMeta.Block_MapReaderAndContextastignLocalReaderAndContext(map));
            //reader.Seek(map.EndPos);
            ary.Add(CommonExpressionMeta.Call_Reader_BufferSeek(Expression.Property(map, nameof(IMapDataSource.EndPosition))));
            //context.Depth--;
            ary.Add(CommonExpressionMeta.Call_DeserializeContext_Depth_Decrementastign);
            //return t;
            ary.Add(Expression.Return(returnTarget, instance));
            //label default(T)
            ary.Add(Expression.Label(returnTarget, instance));

            BlockExpression block = Expression.Block(new ParameterExpression[] { map, instance }, ary.GetArray());
            Deserialize = Expression.Lambda(block, CommonExpressionMeta.Par_Reader, CommonExpressionMeta.Par_DeserializeContext).Compile();
        }

        public static void GenerateInjectCtor(ConstructorInfo constructor, Type injectType)
        {
            Deserialize = Expression.Lambda(CommonExpressionMeta.GenerateInjectCtor(typeof(T), constructor, injectType), CommonExpressionMeta.Par_Reader, CommonExpressionMeta.Par_DeserializeContext).Compile();
        }
    }
}