Esempio n. 1
0
        private MethodBuilder GenerateMethod(GeneratorContext ctx, MethodInfo method, PropertyInfo property = null)
        {
            MethodInfo   methodImpl   = GetImplementation(ctx.ServiceType, ctx.ImplementationType, method);
            PropertyInfo propertyImpl = property != null?GetImplementation(ctx.ServiceType, ctx.ImplementationType, property) : null;

            MethodBuilder methodBuilder = ctx.TypeBuilder.DefineMethod(method.Name,
                                                                       method.Attributes & ~MethodAttributes.Abstract,
                                                                       method.ReturnType,
                                                                       method.GetParameters().Select(o => o.ParameterType).ToArray());

            ctx.GenericArguments      = method.GetGenericArguments();
            ctx.GenericParameterNames = new string[0];
            if (method.IsGenericMethod)
            {
                ctx.GenericParameterNames = ctx.GenericArguments.Select(o => o.Name).ToArray();
                GenericTypeParameterBuilder[] genPar0 = methodBuilder.DefineGenericParameters(ctx.GenericParameterNames);
                for (int i = 0; i < ctx.GenericParameterNames.Length; i++)
                {
                    genPar0[i].SetGenericParameterAttributes(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                             .GetTypeInfo()
#endif
                                                             .GenericParameterAttributes);
                    genPar0[i].SetInterfaceConstraints(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                       .GetTypeInfo()
#endif
                                                       .GetGenericParameterConstraints());
                }
            }

            // cache handler discovery
            CachedAttribute cacheParam = property != null
                                ? FindAttribute <CachedAttribute>(ctx.ServiceType, ctx.ImplementationType, property)
                                : FindAttribute <CachedAttribute>(ctx.ServiceType, ctx.ImplementationType, method);

            ctx.Handler = cacheParam?.Manager;

            bool isGetter = true;
            //bool isAsync = false;
            if (property != null)
            {
                ctx.Handler = null;
            }
            // methods with no result
            if (method.ReturnType == typeof(void))
            {
                //isGetter = false;
                //if (ctx.Property == null)
                ctx.Handler = null;
            }
            // cannot cache the enumerables
            if (method.ReturnType
#if NETSTANDARD1_6
                .GetTypeInfo()
#endif
                .IsGenericType&&
                (method.ReturnType.GetGenericTypeDefinition()
#if NETSTANDARD1_6
                 .GetTypeInfo()
#endif
                 .IsEquivalentTo(typeof(IEnumerable <>))
#if NETSTANDARD2_1
                 || method.ReturnType.GetGenericTypeDefinition()
                 .IsEquivalentTo(typeof(IAsyncEnumerable <>))
#endif
                ))
            {
                ctx.Handler = null;
            }
            // TODO generic methods are not supported yet
            if (method.IsGenericMethod)
            {
                ctx.Handler = null;
            }
            // TODO async methods are not supported yet
            if (method.ReturnType
#if NETSTANDARD1_6
                .GetTypeInfo()
#endif
                .IsEquivalentTo(typeof(Task)) ||
                (method.ReturnType
#if NETSTANDARD1_6
                 .GetTypeInfo()
#endif
                 .BaseType
#if NETSTANDARD1_6
                 ?.GetTypeInfo()
#endif
                 ?.IsEquivalentTo(typeof(Task)) ?? false))
            {
                //isAsync = true;
                ctx.Handler = null;
            }
            // reference and output parameters are not supported (yet?)
            if (method.GetParameters().Any(o => o.ParameterType.IsByRef))
            {
                //isGetter = true;
                ctx.Handler = null;
            }

            // direct proxy test
            //ctx.Handler = null;

            #region Without cache - direct call of non-cached variant

            ILGenerator il = methodBuilder.GetILGenerator();

            if (ctx.Handler == null)
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Call, NonCachedMethod(ctx.ServiceBase));
                int parCount = method.GetParameters().Length;
                if (property != null && !isGetter)
                {
                    parCount++;
                }
                for (int i = 1; i <= parCount; i++)
                {
                    il.Emit_Ldarg(i);
                }
                il.Emit(OpCodes.Callvirt, methodImpl);
                if (property != null && !isGetter)
                {
                    il.Emit(OpCodes.Nop);
                }
                il.Emit(OpCodes.Ret);

                return(methodBuilder);
            }

            if (property != null)
            {
                return(isGetter
                                        ? GeneratePropertyGetMethod(ctx, method, property)
                                        : GeneratePropertySetMethod(ctx, method, property));
            }

            #endregion
            ctx.NestedFields.Clear();
            ctx.NestedKeyFields.Clear();
            GenerateNestedType(ref ctx, method, methodImpl);

            FieldBuilder handler  = GetCacheField(ctx, ctx.Handler);
            LocalBuilder local    = il.DeclareLocal(ctx.NestedType);
            LocalBuilder param    = il.DeclareLocal(typeof(CacheParams));
            FieldInfo    keyField = typeof(CacheParams)
#if NETSTANDARD1_6
                                    .GetTypeInfo()
#endif
                                    .GetField(nameof(CacheParams.Key));
            FieldInfo tagsField = typeof(CacheParams)
#if NETSTANDARD1_6
                                  .GetTypeInfo()
#endif
                                  .GetField(nameof(CacheParams.Tags));
            FieldInfo durField = typeof(CacheParams)
#if NETSTANDARD1_6
                                 .GetTypeInfo()
#endif
                                 .GetField(nameof(CacheParams.Duration));

            il.Emit(OpCodes.Newobj, ctx.NestedType
#if NETSTANDARD1_6
                    .GetTypeInfo()
#endif
                    .GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Stloc_0);
            {
                int idx0 = 1;
                foreach (FieldBuilder field in ctx.NestedFields)
                {
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit_Ldarg(idx0);
                    il.Emit(OpCodes.Stfld, field);
                    idx0++;
                }
            }

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, handler);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Newobj, typeof(CacheParams)
#if NETSTANDARD1_6
                    .GetTypeInfo()
#endif
                    .GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldloc_1);

            #region Generate cache key
            {
                int addBytes = 2;
                if (method.IsGenericMethod)
                {
                    addBytes += ctx.GenericArguments.Length;
                }

                il.Emit_Ldc_I4(ctx.NestedKeyFields.Count + addBytes);
                il.Emit(OpCodes.Newarr, typeof(object));

                string svcKey = ctx.ServiceType
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetCustomAttribute <CacheKeyAttribute>()?.CacheKey
                                ?? ctx.ImplementationType
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetCustomAttribute <CacheKeyAttribute>()?.CacheKey
                                ?? ctx.ImplementationType.ToString();

                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldc_I4_0);
                il.Emit(OpCodes.Ldstr, svcKey);
                il.Emit(OpCodes.Stelem_Ref);

                // add generic type arguments from method
                int idx0 = 1;
                if (method.IsGenericMethod)
                {
                    foreach (Type gen in ctx.GenericArguments)
                    {
                        il.Emit(OpCodes.Dup);
                        il.Emit_Ldc_I4(idx0);
                        il.Emit(OpCodes.Ldtoken, gen);
                        il.Emit(OpCodes.Call, typeof(Type)
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) }));
                        il.Emit(OpCodes.Stelem_Ref);
                        idx0++;
                    }
                }

                string methodKey =
                    (methodImpl.GetCustomAttribute <CacheKeyAttribute>()
                     ?? method.GetCustomAttribute <CacheKeyAttribute>())?.CacheKey
                    ?? method.ToString();

                il.Emit(OpCodes.Dup);
                il.Emit_Ldc_I4(idx0);
                il.Emit(OpCodes.Ldstr, methodKey);
                il.Emit(OpCodes.Stelem_Ref);
                idx0++;

                foreach (FieldBuilder field in ctx.NestedKeyFields)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit_Ldc_I4(idx0);
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ldfld, field);
                    if (field.FieldType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .IsValueType)
                    {
                        il.Emit(OpCodes.Box, field.FieldType);
                    }
                    il.Emit(OpCodes.Stelem_Ref);
                    idx0++;
                }
                il.Emit(OpCodes.Call, GenerateKeyMethod);
                il.Emit(OpCodes.Stfld, keyField);
                il.Emit(OpCodes.Ldloc_1);
            }
            #endregion

            #region Generate tags array
            {
                object[] tags =
                    (property != null
                                                ? propertyImpl.GetCustomAttributes <CacheTagsAttribute>(true).Union(
                         property.GetCustomAttributes <CacheTagsAttribute>(true))
                                                : methodImpl.GetCustomAttributes <CacheTagsAttribute>(true).Union(
                         method.GetCustomAttributes <CacheTagsAttribute>(true))
                    ).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .GetCustomAttributes <CacheTagsAttribute>(true)).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .GetCustomAttributes <CacheTagsAttribute>(true)).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Module.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Module.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Assembly.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Assembly.GetCustomAttributes <CacheTagsAttribute>())
                    .SelectMany(o => o.Tags)
                    .Distinct()
                    .ToArray();

                // pass tags to delegate
                il.Emit_Ldc_I4(tags.Length);
                il.Emit(OpCodes.Newarr, typeof(string));
                int idx0 = 0;
                foreach (object tag in tags)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit_Ldc_I4(idx0);
                    il.Emit(OpCodes.Ldstr, CachedServiceBase.GetComponentKey(tag, -1));
                    il.Emit(OpCodes.Stelem_Ref);
                    idx0++;
                }
                il.Emit(OpCodes.Stfld, tagsField);
                il.Emit(OpCodes.Ldloc_1);
            }
            #endregion

            il.Emit_Ldc_I4(cacheParam?.Durability ?? DefaultDurability);
            il.Emit(OpCodes.Stfld, durField);
            il.Emit(OpCodes.Ldloc_1);

            #region Call delegate
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldftn, ctx.NestedMethod);
                ConstructorInfo lambdaCtor;
                //if (isGetter)
                //{
                lambdaCtor = typeof(Func <,>)
                             .MakeGenericType(ctx.ServiceBase, method.ReturnType)
#if NETSTANDARD1_6
                             .GetTypeInfo()
#endif
                             .GetConstructor(new[] { typeof(object), typeof(IntPtr) });
                //}
                //else
                //{
                //	Type[] genParamTypes = new[] { ctx.ServiceBase }.Union(ctx.NestedFields.Select(o => o.FieldType)).ToArray();
                //	Type lambda = typeof(Action).Assembly.GetTypes()
                //		.Where(o => o.Name == nameof(Action) &&
                //			o.GetGenericArguments().Length == genParamTypes.Length)
                //		.Single();
                //	lambda = lambda.MakeGenericType(genParamTypes);
                //	lambdaCtor = lambda
                //		.GetConstructor(new[] { typeof(object), typeof(IntPtr) });
                //}
                il.Emit(OpCodes.Newobj, lambdaCtor);

                string methodName =
                    //isAsync
                    //? isGetter ? nameof(ICacheManager.GetAsync) : nameof(ICacheManager.SetAsync)
                    //: isGetter ? nameof(ICacheManager.Get) : nameof(ICacheManager.Set);
                    isGetter ? nameof(ICacheManager.Get) : nameof(ICacheManager.Set);

                int parNum = isGetter ? 3 : 4;

                MethodInfo getMethod = typeof(ICacheManager)
#if NETSTANDARD1_6
                                       .GetTypeInfo()
#endif
                                       .GetMethods()
                                       .Where(o => o.IsGenericMethod &&
                                              o.GetGenericArguments().Length == 2 &&
                                              o.GetParameters().Length == parNum &&
                                              o.GetParameters()[1].ParameterType == typeof(CacheParams))
                                       .Single(o => o.Name == methodName);

                il.Emit(OpCodes.Callvirt, getMethod.MakeGenericMethod(new[] { ctx.ServiceBase, method.ReturnType }));
            }
            #endregion

            il.Emit(OpCodes.Ret);
            return(methodBuilder);
        }
Esempio n. 2
0
 public MethodBuilder GeneratePropertySetMethod(GeneratorContext ctx, MethodInfo method, PropertyInfo property = null)
 {
     // TODO generate property setter method
     return(null);
 }
Esempio n. 3
0
        private void GenerateNestedType(ref GeneratorContext ctx, MethodInfo method, MethodInfo methodImpl)
        {
            int idx;

            lock (indexLock)
            {
                idx = index++;
            }

            TypeBuilder builder = ctx.Module.DefineType($"<>c__CacheDelegate_{method.Name}_{idx}",
                                                        TypeAttributes.Class |
                                                        TypeAttributes.NotPublic |
                                                        TypeAttributes.AnsiClass |
                                                        TypeAttributes.AutoClass |
                                                        TypeAttributes.Sealed |
                                                        TypeAttributes.BeforeFieldInit);

            builder.SetCustomAttribute(new CustomAttributeBuilder(
                                           typeof(CompilerGeneratedAttribute)
#if NETSTANDARD1_6
                                           .GetTypeInfo()
#endif
                                           .GetConstructor(Type.EmptyTypes),
                                           new object[0]));

            // add generic parameters
            GenericTypeParameterBuilder[] genPar = null;
            if (method.IsGenericMethod)
            {
                genPar = builder.DefineGenericParameters(ctx.GenericParameterNames);
                for (int i = 0; i < ctx.GenericParameterNames.Length; i++)
                {
                    genPar[i].SetGenericParameterAttributes(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                            .GetTypeInfo()
#endif
                                                            .GenericParameterAttributes);
                    genPar[i].SetInterfaceConstraints(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                      .GetTypeInfo()
#endif
                                                      .GetGenericParameterConstraints());
                }
            }

            //FieldBuilder asyncState = null;
            //FieldBuilder asyncBuilder = null;
            //if (isAsync)
            //{
            //	asyncState = nested.DefineField("<>1__state", typeof(int), FieldAttributes.Public);
            //	asyncBuilder = nested.DefineField("<>t__builder",
            //		typeof(AsyncTaskMethodBuilder<>).MakeGenericType(method.ReturnType),
            //		FieldAttributes.Public);
            //}

            #region Generate nested fields and key array

            List <string> parameters = method.GetParameters()
                                       .Where(o => o.GetCustomAttribute <CacheIgnoreAttribute>() == null)
                                       .Select(o => o.Name).ToList();
            parameters = methodImpl.GetParameters()
                         .Where(o => o.GetCustomAttribute <CacheIgnoreAttribute>() == null)
                         .Where(o => parameters.Contains(o.Name))
                         .Select(o => o.Name).ToList();

            foreach (ParameterInfo par in method.GetParameters())
            {
                FieldBuilder field = null;
#if !NETSTANDARD1_6
                if (genPar != null)
                {
                    GenericTypeParameterBuilder b = genPar.SingleOrDefault(o => o.Name == par.ParameterType.Name);
                    if (b != null)
                    {
                        field = builder.DefineField(par.Name, b, FieldAttributes.Public);
                    }
                }
#else
#endif
                //if (field == null)
                field = builder.DefineField(par.Name, par.ParameterType, FieldAttributes.Public);

                ctx.NestedFields.Add(field);
                if (parameters.Contains(field.Name))
                {
                    ctx.NestedKeyFields.Add(field);
                }
            }

            #endregion

            ctx.NestedMethod = builder.DefineMethod($"<{method.Name}>b__0",
                                                    MethodAttributes.Assembly |
                                                    MethodAttributes.HideBySig,
                                                    method.ReturnType, new[] { ctx.ServiceBase });

            #region Delegate code

            ParameterBuilder parBuilder = ctx.NestedMethod.DefineParameter(1, ParameterAttributes.None, "svc");
            ILGenerator      il         = ctx.NestedMethod.GetILGenerator();
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Callvirt, NonCachedMethod(ctx.ServiceBase));
            foreach (FieldBuilder item in ctx.NestedFields)
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, item);
            }
            il.Emit(OpCodes.Callvirt, methodImpl);
            il.Emit(OpCodes.Ret);

            builder.DefineDefaultConstructor(MethodAttributes.Public);

            Type result = builder.CreateTypeInfo().AsType();

            if (result
#if NETSTANDARD1_6
                .GetTypeInfo()
#endif
                .IsGenericTypeDefinition)
            {
                result.MakeGenericType(method.GetGenericArguments());
            }

            ctx.NestedType = result;

            #endregion
        }