Exemplo n.º 1
0
        private void WeavePropertySetter(MethodDefinition setter, MethodDefinition propertyGet)
        {
            setter.Body.InitLocals = true;
            setter.Body.SimplifyMacros();

            Instruction firstInstruction = setter.Body.Instructions.First();
            ILProcessor processor = setter.Body.GetILProcessor();

            // Add local variables
            int cacheKeyIndex = setter.AddVariable<string>();

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(setter);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            // Create set cache key
            current = current.AppendStloc(processor, cacheKeyIndex);

            current = InjectCacheKeyCreatedCode(setter, current, processor, cacheKeyIndex);

            if (LogDebugOutput)
            {
                current = current.AppendDebugWrite(processor, "Clearing cache.", ModuleDefinition);
            }

            if (!setter.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            current.Append(processor.Create(OpCodes.Call, setter.Module.Import(propertyGet)), processor)
                .AppendLdloc(processor, cacheKeyIndex)
                .Append(
                    processor.Create(OpCodes.Callvirt,
                        setter.Module.Import(CacheTypeGetRemoveMethod(propertyGet.ReturnType.Resolve(), CacheTypeRemoveMethodName))),
                    processor);

            setter.Body.OptimizeMacros();
        }
Exemplo n.º 2
0
        void InjectContext(ILProcessor processor, Instruction firstInstruction, MethodDefinition method, ReferenceContainer references)
        {
            /*
            nop
            ldstr "Namespace.Class.Method"
            ldc.i4.1
            newarr object
            stloc.s CS$0$0001
            ldloc.s CS$0$0001
            ldc.i4.0
            ldarg.1
            box int32
            stelem.ref
            ldloc.s CS$0$0001
            call class [Yalf]Yalf.IContext [Yalf]Yalf.Log::MethodContext(string, object[])
            stloc.0
            */

            contextVar = new VariableDefinition(references.IContextType);
            body.Variables.Insert(0, contextVar);

            int objectParamsArrayIndex = method.AddVariable<object[]>();

            // Generate MethodContext calling method name
            var builder = new StringBuilder();

            builder.Append(string.Join(".",
                method.DeclaringType.Namespace,
                FormatType(method.DeclaringType),
                FormatMethod(method)));

            var current = firstInstruction
                .Prepend(processor.Create(OpCodes.Ldstr, builder.ToString()), processor);

            // Create object[] for MethodContext
            current = current
                .AppendLdcI4(processor, method.Parameters.Count)
                .Append(processor.Create(OpCodes.Newarr, method.Module.ImportType<object>()), processor)
                .AppendStloc(processor, objectParamsArrayIndex);

            var nonStaticMethodAddOne = method.IsStatic ? 0 : 1;

            // Set object[] values
            for (int i = 0; i < method.Parameters.Count; i++)
            {
                current = current
                    .AppendLdloc(processor, objectParamsArrayIndex)
                    .AppendLdcI4(processor, i);

                var paramType = method.Parameters[i].ParameterType;

                if (paramType.MetadataType == MetadataType.UIntPtr ||
                    paramType.MetadataType == MetadataType.FunctionPointer ||
                    paramType.MetadataType == MetadataType.IntPtr ||
                    paramType.MetadataType == MetadataType.Pointer)
                {
                    // don't store pointer types into object[] (we can't ToString them anyway)
                    // store type name as string instead
                    current = current.AppendLdstr(processor, paramType.FullName);
                }
                else
                {
                    current = current
                        .AppendLdarg(processor, i + nonStaticMethodAddOne)
                        .AppendBoxAndResolveRefIfNecessary(processor, paramType);
                }

                current = current.Append(processor.Create(OpCodes.Stelem_Ref), processor);
            }

            // Call Log.MethodContext
            current
                .AppendLdloc(processor, objectParamsArrayIndex)
                .Append(processor.Create(OpCodes.Call, references.MethodContextMethod), processor)
                .AppendStloc(processor, contextVar.Index);
        }
Exemplo n.º 3
0
        private void WeaveMethod(MethodDefinition methodDefinition, MethodDefinition propertyGet)
        {
            methodDefinition.Body.InitLocals = true;

            methodDefinition.Body.SimplifyMacros();

            Instruction firstInstruction = methodDefinition.Body.Instructions.First();

            ICollection<Instruction> returnInstructions =
                methodDefinition.Body.Instructions.ToList().Where(x => x.OpCode == OpCodes.Ret).ToList();

            if (returnInstructions.Count == 0)
            {
                LogWarning(string.Format("Method {0} does not contain any return statement. Skip weaving of method {0}.",
                    methodDefinition.Name));
                return;
            }

            // Add local variables
            int cacheKeyIndex = methodDefinition.AddVariable<string>();
            int resultIndex = methodDefinition.AddVariable(methodDefinition.ReturnType);
            int objectArrayIndex = methodDefinition.AddVariable<object[]>();

            ILProcessor processor = methodDefinition.Body.GetILProcessor();

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(methodDefinition);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            current = SetCacheKeyLocalVariable(current, methodDefinition, processor, cacheKeyIndex, objectArrayIndex);

            current = InjectCacheKeyCreatedCode(methodDefinition, current, processor, cacheKeyIndex);

            TypeDefinition propertyGetReturnTypeDefinition = propertyGet.ReturnType.Resolve();

            if (!methodDefinition.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            MethodReference methodReferenceContain =
                methodDefinition.Module.Import(CacheTypeGetContainsMethod(propertyGetReturnTypeDefinition,
                    CacheTypeContainsMethodName));

            current =
                current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
                    .AppendLdloc(processor, cacheKeyIndex)
                    .Append(processor.Create(OpCodes.Callvirt, methodReferenceContain), processor)
                    .Append(processor.Create(OpCodes.Brfalse, firstInstruction), processor);

            // False branche (store value in cache of each return instruction)
            foreach (Instruction returnInstruction in returnInstructions)
            {
                returnInstruction.Previous.AppendStloc(processor, resultIndex);

                if (LogDebugOutput)
                {
                    returnInstruction.Previous.AppendDebugWrite(processor, "Storing to cache.", methodDefinition.Module);
                }

                if (!methodDefinition.IsStatic)
                {
                    returnInstruction.Previous.AppendLdarg(processor, 0);
                }

                MethodReference methodReferenceReturn =
                    methodDefinition.Module.Import(CacheTypeGetStoreMethod(propertyGetReturnTypeDefinition, CacheTypeStoreMethodName));

                returnInstruction.Previous.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)),
                    processor)
                    .AppendLdloc(processor, cacheKeyIndex)
                    .AppendLdloc(processor, resultIndex)
                    .AppendBoxIfNecessary(processor, methodDefinition.ReturnType)
                    .Append(processor.Create(OpCodes.Callvirt, methodReferenceReturn), processor)
                    .AppendLdloc(processor, resultIndex);
            }

            if (LogDebugOutput)
            {
                current = current.AppendDebugWrite(processor, "Loading from cache.", methodDefinition.Module);
            }

            if (!methodDefinition.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            // Start of branche true
            MethodReference methodReferenceRetrieve =
                methodDefinition.Module.Import(CacheTypeGetRetrieveMethod(propertyGetReturnTypeDefinition,
                    CacheTypeRetrieveMethodName)).MakeGeneric(new[] { methodDefinition.ReturnType });

            current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
                .AppendLdloc(processor, cacheKeyIndex)
                .Append(processor.Create(OpCodes.Callvirt, methodReferenceRetrieve), processor)
                .AppendStloc(processor, resultIndex)
                .Append(processor.Create(OpCodes.Br, returnInstructions.Last().Previous), processor);

            methodDefinition.Body.OptimizeMacros();
        }
        public void Cache(MethodDefinition method, CustomAttribute attribute, TypeDefinition typeDefinition)
        {
            if (method.ReturnType.FullName == method.Module.ImportType(typeof (void)).FullName)
            {
                return;
            }
            if (method.IsConstructor)
            {
                return;
            }

            method.Body.InitLocals = true;
            method.Body.SimplifyMacros();

            if (method.Body.Instructions.All(x => x.OpCode != OpCodes.Ret))
            {
                return;
            }

            // ref to retrieve method and custom attribute
            var getMethodFromHandleRef =
                _referenceFinder.GetMethodReference(typeof (MethodBase),
                    md => md.Name == "GetMethodFromHandle" && md.Parameters.Count == 2);
            var getCustomAttributesRef =
                _referenceFinder.GetMethodReference(typeof (MemberInfo),
                    md => md.Name == "GetCustomAttributes" && md.Parameters.Count == 2);
            var getTypeFromHandleRef = _referenceFinder.GetMethodReference(typeof (Type), md => md.Name == "GetTypeFromHandle");

            // types ref
            var methodBaseTypeRef = _referenceFinder.GetTypeReference(typeof (MethodBase));
            var parameterTypeRef = _referenceFinder.GetTypeReference(typeof (object));
            var parametersArrayTypeRef = _referenceFinder.GetTypeReference(typeof (object[]));

            // variable definitions
            var methodVariableDefinition = method.AddVariable(methodBaseTypeRef, "__fody$method");
            var attributeVariableDefinition = method.AddVariable(attribute.AttributeType, "__fody$attribute");
            var parametersVariableDefinition = method.AddVariable(parametersArrayTypeRef, "__fody$parameters");
            var cachevalVariableDefinition = method.AddVariable(method.ReturnType, "__fody$cacheval");
            var retvalVariableDefinition = method.AddVariable(method.ReturnType, "__fody$retval");

            ILProcessor processor = method.Body.GetILProcessor();
            Instruction methodBodyFirstInstruction = method.Body.Instructions.First();

            var retrieveMethodRef =
                _referenceFinder.GetMethodReference(attribute.AttributeType, md => md.Name == "Retrieve")
                    .MakeGeneric(method.ReturnType);
            var storeMethodRef =
                _referenceFinder.GetMethodReference(attribute.AttributeType, md => md.Name == "Store")
                    .MakeGeneric(method.ReturnType);

            // create instructions
            var getAttributeInstanceInstructions =
                GetAttributeInstanceInstructions(processor, method, attribute, attributeVariableDefinition, methodVariableDefinition,
                    getCustomAttributesRef, getTypeFromHandleRef, getMethodFromHandleRef);
            var createParametersArrayInstructions =
                CreateParametersArrayInstructions(processor, method, parameterTypeRef, parametersVariableDefinition);
            var retrieveInstructions =
                GetCallRetrieveInstructions(processor, attributeVariableDefinition, methodVariableDefinition,
                    parametersVariableDefinition, cachevalVariableDefinition, retrieveMethodRef);
            var saveRetvalInstructions = GetSaveRetvalInstructions(processor, retvalVariableDefinition);
            var storeInstructions =
                GetCallStoreInstructions(processor, attributeVariableDefinition, retvalVariableDefinition, methodVariableDefinition,
                    parametersVariableDefinition, storeMethodRef);
            var methodBodyReturnInstructions = GetMethodBodyReturnInstructions(processor, retvalVariableDefinition);
            var methodBodyReturnInstruction = methodBodyReturnInstructions.First();

            var tmp = GetReturnIfFoundInstructions(processor, cachevalVariableDefinition, methodBodyFirstInstruction);

            ReplaceRetInstructions(processor, saveRetvalInstructions.Concat(storeInstructions).First());

            // insert instructions
            processor.InsertBefore(methodBodyFirstInstruction, getAttributeInstanceInstructions);
            processor.InsertBefore(methodBodyFirstInstruction, createParametersArrayInstructions);
            processor.InsertBefore(methodBodyFirstInstruction, retrieveInstructions);
            processor.InsertAfter(method.Body.Instructions.Last(), methodBodyReturnInstructions);
            processor.InsertBefore(methodBodyReturnInstruction, saveRetvalInstructions);
            processor.InsertBefore(methodBodyReturnInstruction, storeInstructions);
            processor.InsertBefore(methodBodyFirstInstruction, tmp);

            method.Body.OptimizeMacros();
        }