Exemple #1
0
        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);
        }
Exemple #2
0
        /// <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);
        }