public void Decorate(TypeDefinition type, MethodDefinition method, CustomAttribute attribute, bool explicitMatch) { method.Body.InitLocals = true; var methodBaseTypeRef = this._referenceFinder.GetTypeReference(typeof(MethodBase)); var exceptionTypeRef = this._referenceFinder.GetTypeReference(typeof(Exception)); var parameterTypeRef = this._referenceFinder.GetTypeReference(typeof(object)); var parametersArrayTypeRef = new ArrayType(parameterTypeRef); var initMethodRef1 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "Init" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(MethodBase).FullName); var initMethodRef2 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "Init" && md.Parameters.Count == 2 && md.Parameters[0].ParameterType.FullName == typeof(object).FullName); var initMethodRef3 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "Init" && md.Parameters.Count == 3); var needBypassRef0 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "NeedBypass" && md.Parameters.Count == 0); var onEntryMethodRef0 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "OnEntry" && md.Parameters.Count == 0); var onExitMethodRef0 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "OnExit" && md.Parameters.Count == 0); var onExitMethodRef1 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "OnExit" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(object).FullName); var alterRetvalRef1 = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "AlterRetval" && md.Parameters.Count == 1); var onExceptionMethodRef = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "OnException" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(Exception).FullName); var taskContinuationMethodRef = this._referenceFinder.GetOptionalMethodReference(attribute.AttributeType, md => md.Name == "OnTaskContinuation"); var attributeVariableDefinition = AddVariableDefinition(method, "__fody$attribute", attribute.AttributeType); var methodVariableDefinition = AddVariableDefinition(method, "__fody$method", methodBaseTypeRef); VariableDefinition exceptionVariableDefinition = null; VariableDefinition parametersVariableDefinition = null; VariableDefinition retvalVariableDefinition = null; if (initMethodRef3 != null) { parametersVariableDefinition = AddVariableDefinition(method, "__fody$parameters", parametersArrayTypeRef); } if (onExceptionMethodRef != null) { exceptionVariableDefinition = AddVariableDefinition(method, "__fody$exception", exceptionTypeRef); } bool needCatchReturn = null != (onExitMethodRef1 ?? onExitMethodRef0 ?? onExceptionMethodRef ?? taskContinuationMethodRef ?? alterRetvalRef1 ?? needBypassRef0); if (method.ReturnType.FullName != "System.Void" && needCatchReturn) { retvalVariableDefinition = AddVariableDefinition(method, "__fody$retval", method.ReturnType); } MethodBodyRocks.SimplifyMacros(method.Body); var processor = method.Body.GetILProcessor(); var methodBodyFirstInstruction = method.Body.Instructions.First(); if (method.IsConstructor) { var callBase = method.Body.Instructions.FirstOrDefault( i => (i.OpCode == OpCodes.Call) && (i.Operand is MethodReference) && ((MethodReference)i.Operand).Resolve().IsConstructor); methodBodyFirstInstruction = callBase?.Next ?? methodBodyFirstInstruction; } var initAttributeVariable = this.GetAttributeInstanceInstructions(processor, attribute, method, attributeVariableDefinition, methodVariableDefinition, explicitMatch); IEnumerable <Instruction> callInitInstructions = null, createParametersArrayInstructions = null, callOnEntryInstructions = null, saveRetvalInstructions = null, callOnExitInstructions = null; if (parametersVariableDefinition != null) { createParametersArrayInstructions = CreateParametersArrayInstructions( processor, method, parameterTypeRef, parametersVariableDefinition); } var initMethodRef = initMethodRef3 ?? initMethodRef2 ?? initMethodRef1; if (initMethodRef != null) { callInitInstructions = GetCallInitInstructions( processor, type, method, attributeVariableDefinition, methodVariableDefinition, parametersVariableDefinition, initMethodRef); } if (onEntryMethodRef0 != null) { callOnEntryInstructions = GetCallOnEntryInstructions(processor, attributeVariableDefinition, onEntryMethodRef0); } if (retvalVariableDefinition != null) { saveRetvalInstructions = GetSaveRetvalInstructions(processor, retvalVariableDefinition); } if (retvalVariableDefinition != null && onExitMethodRef1 != null) { callOnExitInstructions = GetCallOnExitInstructions(processor, attributeVariableDefinition, onExitMethodRef1, retvalVariableDefinition); } else if (onExitMethodRef0 != null) { callOnExitInstructions = GetCallOnExitInstructions(processor, attributeVariableDefinition, onExitMethodRef0); } IEnumerable <Instruction> methodBodyReturnInstructions = null, tryCatchLeaveInstructions = null, catchHandlerInstructions = null, bypassInstructions = null; if (needCatchReturn) { methodBodyReturnInstructions = GetMethodBodyReturnInstructions(processor, attributeVariableDefinition, retvalVariableDefinition, alterRetvalRef1); if (needBypassRef0 != null) { bypassInstructions = GetBypassInstructions(processor, attributeVariableDefinition, needBypassRef0, methodBodyReturnInstructions.First()); } if (onExceptionMethodRef != null) { tryCatchLeaveInstructions = GetTryCatchLeaveInstructions(processor, methodBodyReturnInstructions.First()); catchHandlerInstructions = GetCatchHandlerInstructions(processor, attributeVariableDefinition, exceptionVariableDefinition, onExceptionMethodRef); } ReplaceRetInstructions(processor, saveRetvalInstructions?.FirstOrDefault() ?? callOnExitInstructions?.FirstOrDefault() ?? methodBodyReturnInstructions.First()); } processor.InsertBefore(methodBodyFirstInstruction, initAttributeVariable); if (createParametersArrayInstructions != null) { processor.InsertBefore(methodBodyFirstInstruction, createParametersArrayInstructions); } if (callInitInstructions != null) { processor.InsertBefore(methodBodyFirstInstruction, callInitInstructions); } if (bypassInstructions != null) { processor.InsertBefore(methodBodyFirstInstruction, bypassInstructions); } if (callOnEntryInstructions != null) { processor.InsertBefore(methodBodyFirstInstruction, callOnEntryInstructions); } if (methodBodyReturnInstructions != null) { processor.InsertAfter(method.Body.Instructions.Last(), methodBodyReturnInstructions); if (saveRetvalInstructions != null) { processor.InsertBefore(methodBodyReturnInstructions.First(), saveRetvalInstructions); } if (taskContinuationMethodRef != null) { var taskContinuationInstructions = GetTaskContinuationInstructions( processor, retvalVariableDefinition, attributeVariableDefinition, taskContinuationMethodRef); processor.InsertBefore(methodBodyReturnInstructions.First(), taskContinuationInstructions); } if (callOnExitInstructions != null) { processor.InsertBefore(methodBodyReturnInstructions.First(), callOnExitInstructions); } if (onExceptionMethodRef != null) { processor.InsertBefore(methodBodyReturnInstructions.First(), tryCatchLeaveInstructions); processor.InsertBefore(methodBodyReturnInstructions.First(), catchHandlerInstructions); method.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch) { CatchType = exceptionTypeRef, TryStart = methodBodyFirstInstruction, TryEnd = tryCatchLeaveInstructions.Last().Next, HandlerStart = catchHandlerInstructions.First(), HandlerEnd = catchHandlerInstructions.Last().Next }); } } MethodBodyRocks.OptimizeMacros(method.Body); }
/// <summary> /// Compiles the source body to a CIL method body. /// </summary> /// <returns>A CIL method body.</returns> public Mono.Cecil.Cil.MethodBody Compile() { // Create a method body. var result = new Mono.Cecil.Cil.MethodBody(Method); // Figure out which 'alloca' values can be replaced // by local variables. Usually, that's all of them. var sourceGraph = SourceBody.Implementation; var allocaToVarMap = AllocasToVariables(sourceGraph); // Select instructions. var selector = new CilInstructionSelector(Method, TypeEnvironment, allocaToVarMap); var streamBuilder = new LinearInstructionStreamBuilder <CilCodegenInstruction>( selector); var codegenInsns = streamBuilder.ToInstructionStream(sourceGraph); codegenInsns = OptimizeRegisterAccesses(codegenInsns); // Find the set of loaded values so we can allocate registers to them. var loadedValues = new HashSet <ValueTag>( codegenInsns .SelectMany(insn => insn.Traversal) .OfType <CilLoadRegisterInstruction>() .Select(insn => insn.Value)); loadedValues.UnionWith( codegenInsns .SelectMany(insn => insn.Traversal) .OfType <CilAddressOfRegisterInstruction>() .Select(insn => insn.Value)); // Allocate registers to values. var regAllocator = new CilRegisterAllocator( loadedValues, GetPreallocatedRegisters(sourceGraph), Method.Module); var regAllocation = regAllocator.Analyze(sourceGraph); // Synthesize the actual method body. var processor = result.GetILProcessor(); var emitter = new CodegenEmitter(processor, regAllocation); emitter.Emit(codegenInsns); // Add local variables to method body. Put most popular // locals first to minimize the number of long-form ldloc/stloc // instructions. result.InitLocals = true; foreach (var pair in emitter.RegisterUseCounts.OrderByDescending(pair => pair.Value)) { result.Variables.Add(pair.Key); } foreach (var local in allocaToVarMap.Values) { result.Variables.Add(local); } foreach (var temp in selector.Temporaries) { result.Variables.Add(temp); } // Apply peephole optimizations to the generated method body. CilPeepholeOptimizer.Optimize(result); // Apply Cecil's macro optimizations to the generated method body. MethodBodyRocks.Optimize(result); return(result); }