/// <summary> /// Need to do this so we retain pointer from original first instruction to new first instruction (nop) /// for dbg file to point to it so VS debugger will step into weaved methods /// </summary> /// <param name="ilProcessor"></param> /// <param name="firstInstruction"></param> /// <returns></returns> private static Instruction RejigFirstInstruction(ILProcessor ilProcessor, Instruction firstInstruction) { /* * From: * opcode operand <-- pdb first line pointer * * To: * nop <-- pdb first line pointer * opcode operand <-- cloned second acting as first * */ // clone first instruction which will be used as actual var clonedSecond = CloneInstruction(firstInstruction); clonedSecond.Offset++; var sampleNop = ilProcessor.Create(OpCodes.Nop); // change actual first instruction to NOP firstInstruction.OpCode = sampleNop.OpCode; firstInstruction.Operand = sampleNop.Operand; // append second instruction which now is same as first one used to be at the start of this method // and actual first one is nop firstInstruction.Append(clonedSecond, ilProcessor); // return cloned second as new first instruction return(clonedSecond); }
public static Instruction AppendBoxIfNecessary(this Instruction instruction, ILProcessor processor, TypeReference typeReference) { if (typeReference.IsValueType || typeReference.IsGenericParameter) { return(instruction.Append(processor.Create(OpCodes.Box, typeReference), processor)); } return(instruction); }
public static Instruction AppendBoxAndResolveRefIfNecessary(this Instruction instruction, ILProcessor processor, TypeReference typeReference) { // some of the following code is partially based on IL injection blog post from Girish Jain if (typeReference.IsByReference) { // it might throw an exception, prefer for weaving to fail in such case var referencedTypeSpec = (TypeSpecification)typeReference; var byRefLoadOpCode = GetByRefLoadOpCode(referencedTypeSpec.ElementType.MetadataType); if (byRefLoadOpCode.HasValue) { return(instruction .Append(processor.Create(byRefLoadOpCode.Value), processor) .Append(processor.Create(OpCodes.Box, referencedTypeSpec.ElementType), processor)); } else if (referencedTypeSpec.ElementType.IsValueType) { // no "known" mapping from referenced metadata type // check if we have value type and default to Ldobj return(instruction .Append(processor.Create(OpCodes.Ldobj, referencedTypeSpec.ElementType), processor) .Append(processor.Create(OpCodes.Box, referencedTypeSpec.ElementType), processor)); } // It is a ref reference type so just use reference the pointer return(instruction.Append(processor.Create(OpCodes.Ldind_Ref), processor)); } else if (typeReference.IsValueType || typeReference.IsGenericParameter) { // If it is a value type then you need to box the instance as we are going // to add it to an array which is of type object (reference type) // ------------------------------------------------------------ // Box the parameter type return(instruction.Append(processor.Create(OpCodes.Box, typeReference), processor)); } return(instruction); }
private void WeavePropertySetter(MethodDefinition setter, MethodReference propertyGet) { setter.Body.InitLocals = true; setter.Body.SimplifyMacros(); if (propertyGet.DeclaringType.HasGenericParameters) { propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast <TypeReference>().ToArray()); } Instruction firstInstruction = setter.Body.Instructions.First(); ILProcessor processor = setter.Body.GetILProcessor(); // Add local variables int cacheKeyIndex = setter.AddVariable(ModuleDefinition.TypeSystem.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 = AppendDebugWrite(current, processor, "Clearing cache."); } if (!propertyGet.Resolve().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(); }
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(); }
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(ModuleDefinition.TypeSystem.String); int resultIndex = methodDefinition.AddVariable(methodDefinition.ReturnType); int objectArrayIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.Object.MakeArrayType()); 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) { AppendDebugWrite(returnInstruction.Previous, processor, "Storing to cache."); } 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 = AppendDebugWrite(current, processor, "Loading from cache."); } 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 static Instruction AppendStloc(this Instruction instruction, ILProcessor processor, int index) { return(instruction.Append(processor.Create(OpCodes.Stloc, index), processor)); }
public static Instruction AppendLdstr(this Instruction instruction, ILProcessor processor, string str) { return(instruction.Append(processor.Create(OpCodes.Ldstr, str), processor)); }
public static Instruction AppendLdcI4(this Instruction instruction, ILProcessor processor, int value) { return(instruction.Append(processor.Create(OpCodes.Ldc_I4, value), processor)); }
public static Instruction AppendLd(this Instruction instruction, ILProcessor processor, CustomAttributeArgument argument) { // Tested with the following types (see ECMA-334, 24.1.3 attribute parameter types) // bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort. // object // System.Type // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2) // Single-dimensional arrays of the above types TypeReference argumentType = argument.Type; switch (argumentType.MetadataType) { case MetadataType.ValueType: if (argumentType.Resolve().IsEnum == false) { throw new ArgumentException("Type Enum expected.", "argument"); } // Get underlying Enum type argumentType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType; break; case MetadataType.Object: return(instruction.AppendLd(processor, (CustomAttributeArgument)argument.Value)); case MetadataType.Array: CustomAttributeArgument[] values = (CustomAttributeArgument[])argument.Value; instruction = instruction .AppendLdcI4(processor, values.Length) .Append(processor.Create(OpCodes.Newarr, argument.Type.GetElementType()), processor); TypeReference arrayType = argument.Type.GetElementType(); for (int i = 0; i < values.Length; i++) { instruction = instruction .AppendDup(processor) .AppendLdcI4(processor, i) .AppendLd(processor, values[i]); if (arrayType == ReferenceFinder.Weaver.ModuleDefinition.TypeSystem.Object) { instruction = instruction.AppendBoxIfNecessary(processor, ((CustomAttributeArgument)values[i].Value).Type); } else if (argumentType.Resolve().IsEnum) { // Get underlying Enum type arrayType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType; } if (arrayType.IsValueType) { switch (arrayType.MetadataType) { case MetadataType.Boolean: case MetadataType.SByte: case MetadataType.Byte: instruction = instruction.Append(processor.Create(OpCodes.Stelem_I1), processor); break; case MetadataType.Char: case MetadataType.Int16: case MetadataType.UInt16: instruction = instruction.Append(processor.Create(OpCodes.Stelem_I2), processor); break; case MetadataType.Int32: case MetadataType.UInt32: instruction = instruction.Append(processor.Create(OpCodes.Stelem_I4), processor); break; case MetadataType.Int64: case MetadataType.UInt64: instruction = instruction.Append(processor.Create(OpCodes.Stelem_I8), processor); break; case MetadataType.Single: instruction = instruction.Append(processor.Create(OpCodes.Stelem_R4), processor); break; case MetadataType.Double: instruction = instruction.Append(processor.Create(OpCodes.Stelem_R8), processor); break; default: throw new ArgumentException("Unrecognized array value type.", "argument"); } } else { instruction = instruction.Append(processor.Create(OpCodes.Stelem_Ref), processor); } } return(instruction); } switch (argumentType.MetadataType) { case MetadataType.Boolean: case MetadataType.SByte: return(instruction.Append(processor.Create(OpCodes.Ldc_I4_S, Convert.ToSByte(argument.Value)), processor)); case MetadataType.Int16: return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt16(argument.Value)), processor)); case MetadataType.Int32: return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor)); case MetadataType.Int64: return(instruction.Append(processor.Create(OpCodes.Ldc_I8, Convert.ToInt64(argument.Value)), processor)); case MetadataType.Byte: return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor)); case MetadataType.UInt16: unchecked { return(instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int16)(UInt16)argument.Value), processor)); } case MetadataType.Char: return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToChar(argument.Value)), processor)); case MetadataType.UInt32: unchecked { return(instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int32)(UInt32)argument.Value), processor)); } case MetadataType.UInt64: unchecked { return(instruction.Append(processor.Create(OpCodes.Ldc_I8, (Int64)(UInt64)argument.Value), processor)); } case MetadataType.Single: return(instruction.Append(processor.Create(OpCodes.Ldc_R4, Convert.ToSingle(argument.Value)), processor)); case MetadataType.Double: return(instruction.Append(processor.Create(OpCodes.Ldc_R8, Convert.ToDouble(argument.Value)), processor)); case MetadataType.String: return(instruction.Append(processor.Create(OpCodes.Ldstr, (string)argument.Value), processor)); case MetadataType.Class: if (argumentType.Resolve().IsDefinition == false) { throw new ArgumentException("Type Type expected.", "argument"); } return (instruction .Append(processor.Create(OpCodes.Ldtoken, (TypeReference)argument.Value), processor) .Append(processor.Create(OpCodes.Call, ReferenceFinder.Weaver.ModuleDefinition.ImportMethod( ReferenceFinder.SystemTypeGetTypeFromHandleMethod)), processor)); default: throw new ArgumentException("Unrecognized attribute parameter type.", "argument"); } }
public static Instruction AppendDup(this Instruction instruction, ILProcessor processor) { return(instruction.Append(processor.Create(OpCodes.Dup), processor)); }
private void WeaveMethod(MethodDefinition methodDefinition, CustomAttribute attribute, MethodReference propertyGet) { methodDefinition.Body.InitLocals = true; methodDefinition.Body.SimplifyMacros(); if (propertyGet.DeclaringType.HasGenericParameters) { propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast <TypeReference>().ToArray()); } 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(ModuleDefinition.TypeSystem.String); int resultIndex = methodDefinition.AddVariable(methodDefinition.ReturnType); int objectArrayIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.Object.MakeArrayType()); 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 (!propertyGet.Resolve().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) { AppendDebugWrite(returnInstruction.Previous, processor, "Storing to cache."); } if (!propertyGet.Resolve().IsStatic) { returnInstruction.Previous.AppendLdarg(processor, 0); } MethodReference methodReferenceStore = 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); // Pass parameters to Store method if supported if (methodReferenceStore.Parameters.Count == 3) { returnInstruction.Previous .Append(processor.Create(OpCodes.Newobj, methodDefinition.Module.Import(References.DictionaryConstructor)), processor); foreach (CustomAttributeNamedArgument property in attribute.Properties.Union(attribute.Fields)) { returnInstruction.Previous .AppendDup(processor) .AppendLdstr(processor, property.Name) .AppendLd(processor, property.Argument, References) .AppendBoxIfNecessary(processor, property.Argument.Type != ModuleDefinition.TypeSystem.Object ? property.Argument.Type : ((CustomAttributeArgument)property.Argument.Value).Type) .Append(processor.Create(OpCodes.Callvirt, methodDefinition.Module.Import(References.DictionaryAddMethod)), processor); } } returnInstruction.Previous .Append(processor.Create(OpCodes.Callvirt, methodReferenceStore), processor) .AppendLdloc(processor, resultIndex); } if (LogDebugOutput) { current = AppendDebugWrite(current, processor, "Loading from cache."); } if (!propertyGet.Resolve().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(); }