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(); }
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); }
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(); }