private static ILProcessorContext CreateTupleConstructorCalls(MethodWeavingContext methodWeavingContext,
                                                                      ILProcessorContext processorContext, GenericInstanceType tupleType)
        {
            if (methodWeavingContext == null)
            {
                throw new ArgumentNullException(nameof(methodWeavingContext));
            }

            if (processorContext == null)
            {
                throw new ArgumentNullException(nameof(processorContext));
            }

            if (tupleType == null)
            {
                throw new ArgumentNullException(nameof(tupleType));
            }

            TypeReference[] typeReferences = tupleType.GenericArguments.Cast <TypeReference>().ToArray();

            if (typeReferences.Length == 8)
            {
                processorContext = MemoryCache.CreateTupleConstructorCalls(methodWeavingContext, processorContext,
                                                                           (GenericInstanceType)typeReferences.Last());
            }

            MethodReference tupleConstructor =
                methodWeavingContext.ClassWeavingContext.References.GetSystemTupleConstructor(typeReferences);

            return(processorContext.Append(x => x.Create(OpCodes.Newobj, tupleConstructor)));
        }
        public static void WeaveSetBeforeReturns(MethodWeavingContext methodWeavingContext)
        {
            MethodBody methodDefinitionBody = methodWeavingContext.MethodDefinition.Body;

            List <Instruction> returns   = methodDefinitionBody.Instructions.Where(x => x.OpCode == OpCodes.Ret).ToList();
            ILProcessor        processor = methodDefinitionBody.GetILProcessor();

            // TODO: Skip "ret" created above
            foreach (Instruction returnInstruction in returns.Skip(1))
            {
                ILProcessorContext processorContext = processor.Before(returnInstruction)
                                                      .Append(x => x.Create(OpCodes.Stloc, methodWeavingContext.ResultVariableIndex.Value));

                processorContext = processorContext.Append(x => x.Create(OpCodes.Ldarg_0))
                                   .Append(x => x.Create(OpCodes.Call, methodWeavingContext.ClassWeavingContext.CacheGetterMethod))
                                   .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.CacheKeyVariableIndex.Value))
                                   .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.ResultVariableIndex.Value))
                                   .Append(x => x.Create(OpCodes.Call,
                                                         methodWeavingContext.ClassWeavingContext.References.GetGenericSetMethod(methodWeavingContext.MethodDefinition
                                                                                                                                 .ReturnType)));

                // Not necessary, just return the return value from .Set
                ////.Append(x => x.Create(OpCodes.Pop))
                ////.Append(x => x.Create(OpCodes.Ldloc, index));
            }
        }
        public override void Execute()
        {
            References references = Fody.References.Init(this);

            foreach (WeavingCandidate weavingCandidate in ModuleDefinition.GetWeavingCandidates(references))
            {
                if (weavingCandidate.ClassDefinition.HasCacheAttribute(references) && !weavingCandidate.MethodDefinitions
                    .Where(x => !x.HasNoCacheAttribute(references))
                    .Any(x => x.IsEligibleForWeaving(references)))
                {
                    WriteWarning(
                        $"Class {weavingCandidate.ClassDefinition.Resolve().FullName} contains [Cache] attribute but does not contain eligible methods for caching");
                    continue;
                }

                if (!weavingCandidate.ClassDefinition.IsEligibleForWeaving(references))
                {
                    WriteWarning(
                        $"Class {weavingCandidate.ClassDefinition.Name} contains [Cache] attribute but does not contain a single property implementing IMemoryCache interface");
                    continue;
                }

                ClassWeavingContext classWeavingContext = new ClassWeavingContext(weavingCandidate.ClassDefinition, references);
                classWeavingContext.CacheGetterMethod = MemoryCache.GetCacheGetterMethod(classWeavingContext);

                foreach (MethodDefinition methodDefinition in weavingCandidate.MethodDefinitions)
                {
                    if (!methodDefinition.IsEligibleForWeaving(references))
                    {
                        // Show warning if test was marked explicitly
                        if (methodDefinition.HasCacheAttribute(references))
                        {
                            WriteWarning($"Method {methodDefinition.FullName} contains [Cache] attribute but is not eligible for weaving");
                            break;
                        }

                        continue;
                    }

                    if (methodDefinition.HasNoCacheAttribute(references))
                    {
                        continue;
                    }

                    MethodWeavingContext methodWeavingContext = new MethodWeavingContext(classWeavingContext, methodDefinition);

                    MemoryCache.AddMethodVariables(methodWeavingContext);
                    ILProcessorContext processorContext = MemoryCache.WeaveCreateKey(methodWeavingContext);
                    MemoryCache.WeaveTryGetValueAndReturn(methodWeavingContext, processorContext);
                    MemoryCache.WeaveSetBeforeReturns(methodWeavingContext);
                }
            }
        }
        public override void Execute()
        {
            References references = Fody.References.Init(this);

            // TODO: Extract to method
            var elementsToCache = ModuleDefinition.Types.Select(type =>
            {
                if (type.HasCacheAttribute(references))
                {
                    return(new { Type = type, Methods = type.Methods.ToList() });
                }

                return(new { Type = type, Methods = type.Methods.Where(method => method.HasCacheAttribute(references)).ToList(), });
            })
                                  .Where(x => x.Methods.Any())
                                  .ToList();

            foreach (var elementToCache in elementsToCache)
            {
                if (!elementToCache.Type.IsEligibleForWeaving(references))
                {
                    // TODO: Create warning
                    continue;
                }

                ClassWeavingContext classWeavingContext = new ClassWeavingContext(elementToCache.Type, references);
                classWeavingContext.CacheGetterMethod = MemoryCache.GetCacheGetterMethod(classWeavingContext);

                foreach (MethodDefinition methodDefinition in elementToCache.Methods)
                {
                    if (!methodDefinition.IsEligibleForWeaving(references))
                    {
                        // TODO: Create warning if marked explicit for weaving
                        continue;
                    }

                    MethodWeavingContext methodWeavingContext = new MethodWeavingContext(classWeavingContext, methodDefinition);

                    MemoryCache.AddMethodVariables(methodWeavingContext);
                    ILProcessorContext processorContext = MemoryCache.WeaveCreateKey(methodWeavingContext);
                    MemoryCache.WeaveTryGetValueAndReturn(methodWeavingContext, processorContext);
                    MemoryCache.WeaveSetBeforeReturns(methodWeavingContext);
                }
            }
        }
        public static ILProcessorContext WeaveCreateKey(MethodWeavingContext methodWeavingContext)
        {
            if (methodWeavingContext == null)
            {
                throw new ArgumentNullException(nameof(methodWeavingContext));
            }

            ILProcessorContext processorContext = methodWeavingContext.MethodDefinition.Body.GetILProcessor().Start();

            string methodName = MemoryCache.CreateCacheKeyMethodName(methodWeavingContext.MethodDefinition);

            processorContext = processorContext.Append(x => x.Create(OpCodes.Ldstr, methodName));

            foreach (GenericParameter genericParameter in methodWeavingContext.ClassWeavingContext.TypeDefinition.GenericParameters)
            {
                processorContext = processorContext.Append(x => x.Create(OpCodes.Ldtoken, genericParameter))
                                   .Append(x => x.Create(OpCodes.Call, methodWeavingContext.ClassWeavingContext.References.GetTypeFromHandleMethod));
            }

            foreach (GenericParameter genericParameter in methodWeavingContext.MethodDefinition.GenericParameters)
            {
                processorContext = processorContext.Append(x => x.Create(OpCodes.Ldtoken, genericParameter))
                                   .Append(x => x.Create(OpCodes.Call, methodWeavingContext.ClassWeavingContext.References.GetTypeFromHandleMethod));
            }

            for (int i = 0; i < methodWeavingContext.MethodDefinition.Parameters.Count; i++)
            {
                int value = i;

                processorContext = processorContext.Append(x => x.Create(OpCodes.Ldarg, value + 1));
            }

            return(MemoryCache
                   .CreateTupleConstructorCalls(methodWeavingContext, processorContext, (GenericInstanceType)methodWeavingContext.CacheKeyType)
                   .Append(x => x.Create(OpCodes.Stloc, methodWeavingContext.CacheKeyVariableIndex.Value)));
        }
        public static void WeaveTryGetValueAndReturn(MethodWeavingContext methodWeavingContext, ILProcessorContext processorContext)
        {
            if (methodWeavingContext == null)
            {
                throw new ArgumentNullException(nameof(methodWeavingContext));
            }

            if (processorContext == null)
            {
                throw new ArgumentNullException(nameof(processorContext));
            }

            processorContext = processorContext.Append(x => x.Create(OpCodes.Ldarg_0))
                               .Append(x => x.Create(OpCodes.Call, methodWeavingContext.ClassWeavingContext.CacheGetterMethod))
                               .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.CacheKeyVariableIndex.Value))
                               .Append(x => x.Create(OpCodes.Ldloca, methodWeavingContext.ResultVariableIndex.Value))
                               .Append(x => x.Create(OpCodes.Call,
                                                     methodWeavingContext.ClassWeavingContext.References.GetTryGetValue(methodWeavingContext.MethodDefinition.ReturnType)));

            Instruction instructionNext = processorContext.CurrentInstruction.Next;

            processorContext.Append(x => x.Create(OpCodes.Brfalse, instructionNext))
            .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.ResultVariableIndex.Value))
            .Append(x => x.Create(OpCodes.Ret));
        }
Exemple #7
0
        public static void WeaveSetBeforeReturns(MethodWeavingContext methodWeavingContext)
        {
            if (methodWeavingContext == null)
            {
                throw new ArgumentNullException(nameof(methodWeavingContext));
            }

            CustomAttribute cacheAttribute =
                methodWeavingContext.MethodDefinition.TryGetCacheAttribute(methodWeavingContext.ClassWeavingContext.References);

            if (cacheAttribute == null)
            {
                cacheAttribute =
                    methodWeavingContext.ClassWeavingContext.TypeDefinition.TryGetCacheAttribute(methodWeavingContext.ClassWeavingContext
                                                                                                 .References);
            }

            MethodBody methodDefinitionBody = methodWeavingContext.MethodDefinition.Body;

            List <Instruction> returns   = methodDefinitionBody.Instructions.Where(x => x.OpCode == OpCodes.Ret).ToList();
            ILProcessor        processor = methodDefinitionBody.GetILProcessor();

            // TODO: Skip "ret" created above
            foreach (Instruction returnInstruction in returns.Skip(1))
            {
                ILProcessorContext processorContext = processor.Before(returnInstruction)
                                                      .Append(x => x.Create(OpCodes.Stloc, methodWeavingContext.ResultVariableIndex.Value));

                processorContext = processorContext.Append(x => x.Create(OpCodes.Ldarg_0))
                                   .Append(x => x.Create(OpCodes.Call, methodWeavingContext.ClassWeavingContext.CacheGetterMethod))
                                   .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.CacheKeyVariableIndex.Value))
                                   .Append(x => x.Create(OpCodes.Ldloc, methodWeavingContext.ResultVariableIndex.Value));

                if (cacheAttribute.Properties.Any())
                {
                    processorContext = processorContext.Append(x => x.Create(OpCodes.Newobj,
                                                                             methodWeavingContext.ClassWeavingContext.References.MemoryCacheEntryOptionsConstructor));

                    foreach (CustomAttributeNamedArgument customAttributeNamedArgument in cacheAttribute.Properties)
                    {
                        switch (customAttributeNamedArgument.Name)
                        {
                        case "AbsoluteExpirationRelativeToNow":
                            processorContext = processorContext.Append(x => x.Create(OpCodes.Dup))
                                               .Append(x => x.Create(OpCodes.Ldc_R8, (double)customAttributeNamedArgument.Argument.Value))
                                               .Append(x => x.Create(OpCodes.Call,
                                                                     methodWeavingContext.ClassWeavingContext.References.TimeSpanFromSecondsMethod))
                                               .Append(x => x.Create(OpCodes.Newobj,
                                                                     methodWeavingContext.ClassWeavingContext.References.NullableTimeSpanConstructor))
                                               .Append(x => x.Create(OpCodes.Callvirt,
                                                                     methodWeavingContext.ClassWeavingContext.References.MemoryCacheEntryOptionsAbsoluteExpirationRelativeToNowSetter));
                            break;

                        case "SlidingExpiration":
                            processorContext = processorContext.Append(x => x.Create(OpCodes.Dup))
                                               .Append(x => x.Create(OpCodes.Ldc_R8, (double)customAttributeNamedArgument.Argument.Value))
                                               .Append(x => x.Create(OpCodes.Call,
                                                                     methodWeavingContext.ClassWeavingContext.References.TimeSpanFromSecondsMethod))
                                               .Append(x => x.Create(OpCodes.Newobj,
                                                                     methodWeavingContext.ClassWeavingContext.References.NullableTimeSpanConstructor))
                                               .Append(x => x.Create(OpCodes.Callvirt,
                                                                     methodWeavingContext.ClassWeavingContext.References.MemoryCacheEntryOptionsSlidingExpirationSetter));
                            break;

                        case "Priority":
                            processorContext = processorContext.Append(x => x.Create(OpCodes.Dup))
                                               .Append(x => x.Create(OpCodes.Ldc_I4, (int)customAttributeNamedArgument.Argument.Value))
                                               .Append(x => x.Create(OpCodes.Callvirt,
                                                                     methodWeavingContext.ClassWeavingContext.References.MemoryCacheEntryOptionsPrioritySetter));
                            break;
                        }
                    }

                    processorContext = processorContext.Append(x => x.Create(OpCodes.Call,
                                                                             methodWeavingContext.ClassWeavingContext.References.GetGenericSetMethodWithMemoryCacheEntryOptions(
                                                                                 methodWeavingContext.MethodDefinition.ReturnType)));
                }
                else
                {
                    processorContext = processorContext.Append(x => x.Create(OpCodes.Call,
                                                                             methodWeavingContext.ClassWeavingContext.References.GetGenericSetMethod(methodWeavingContext.MethodDefinition
                                                                                                                                                     .ReturnType)));
                }

                // Not necessary, just return the return value from .Set
                ////.Append(x => x.Create(OpCodes.Pop))
                ////.Append(x => x.Create(OpCodes.Ldloc, index));
            }
        }