Beispiel #1
0
    public void Decorate(TypeDefinition type, MethodDefinition method, CustomAttribute attribute, bool explicitMatch)
    {
        method.Body.InitLocals = true;

        var parametersArrayTypeRef = new ArrayType(objectTypeRef);

        var initMethodRef1 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                        md => md.Name == "Init" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(MethodBase).FullName);
        var initMethodRef2 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                        md => md.Name == "Init" && md.Parameters.Count == 2 && md.Parameters[0].ParameterType.FullName == typeof(object).FullName);
        var initMethodRef3 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                        md => md.Name == "Init" && md.Parameters.Count == 3);

        var needBypassRef0 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                        md => md.Name == "NeedBypass" && md.Parameters.Count == 0);

        var onEntryMethodRef0 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                           md => md.Name == "OnEntry" && md.Parameters.Count == 0);

        var onExitMethodRef0 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                          md => md.Name == "OnExit" && md.Parameters.Count == 0);
        var onExitMethodRef1 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                          md => md.Name == "OnExit" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(object).FullName);

        var alterRetvalRef1 = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                         md => md.Name == "AlterRetval" && md.Parameters.Count == 1);

        var onExceptionMethodRef = referenceFinder.GetOptionalMethodReference(attribute.AttributeType,
                                                                              md => md.Name == "OnException" && md.Parameters.Count == 1 && md.Parameters[0].ParameterType.FullName == typeof(Exception).FullName);

        var taskContinuationMethodRef = 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);
        }

        var needCatchReturn = null != (onExitMethodRef1 ?? onExitMethodRef0 ?? onExceptionMethodRef ?? taskContinuationMethodRef ?? alterRetvalRef1 ?? needBypassRef0);

        if (method.ReturnType.FullName != "System.Void" && needCatchReturn)
        {
            retvalVariableDefinition = AddVariableDefinition(method, "__fody$retval", method.ReturnType);
        }

        method.Body.SimplifyMacros();

        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 reference) &&
                reference.Resolve().IsConstructor);

            methodBodyFirstInstruction = callBase?.Next ?? methodBodyFirstInstruction;
        }

        var initAttributeVariable = GetAttributeInstanceInstructions(processor,
                                                                     attribute,
                                                                     method,
                                                                     attributeVariableDefinition,
                                                                     methodVariableDefinition,
                                                                     explicitMatch);

        List <Instruction> callInitInstructions = null;
        List <Instruction> createParametersArrayInstructions = null;
        List <Instruction> callOnEntryInstructions           = null;
        List <Instruction> saveRetvalInstructions            = null;
        List <Instruction> callOnExitInstructions            = null;

        if (parametersVariableDefinition != null)
        {
            createParametersArrayInstructions = CreateParametersArrayInstructions(
                processor,
                method,
                parametersVariableDefinition).ToList();
        }

        var initMethodRef = initMethodRef3 ?? initMethodRef2 ?? initMethodRef1;

        if (initMethodRef != null)
        {
            callInitInstructions = GetCallInitInstructions(
                processor,
                type,
                method,
                attributeVariableDefinition,
                methodVariableDefinition,
                parametersVariableDefinition,
                initMethodRef).ToList();
        }

        if (onEntryMethodRef0 != null)
        {
            callOnEntryInstructions = GetCallOnEntryInstructions(processor, attributeVariableDefinition, onEntryMethodRef0).ToList();
        }

        if (retvalVariableDefinition != null)
        {
            saveRetvalInstructions = GetSaveRetvalInstructions(processor, retvalVariableDefinition).ToList();
        }

        if (retvalVariableDefinition != null && onExitMethodRef1 != null)
        {
            callOnExitInstructions = GetCallOnExitInstructions(processor, attributeVariableDefinition, onExitMethodRef1, retvalVariableDefinition).ToList();
        }
        else if (onExitMethodRef0 != null)
        {
            callOnExitInstructions = GetCallOnExitInstructions(processor, attributeVariableDefinition, onExitMethodRef0).ToList();
        }

        List <Instruction> methodBodyReturnInstructions = null;
        List <Instruction> tryCatchLeaveInstructions    = null;
        List <Instruction> catchHandlerInstructions     = null;
        List <Instruction> bypassInstructions           = null;

        if (needCatchReturn)
        {
            methodBodyReturnInstructions = GetMethodBodyReturnInstructions(processor, attributeVariableDefinition, retvalVariableDefinition, alterRetvalRef1).ToList();

            if (needBypassRef0 != null)
            {
                bypassInstructions = GetBypassInstructions(processor, attributeVariableDefinition, needBypassRef0, methodBodyReturnInstructions.First()).ToList();
            }

            if (onExceptionMethodRef != null)
            {
                tryCatchLeaveInstructions = GetTryCatchLeaveInstructions(processor, methodBodyReturnInstructions.First()).ToList();
                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
                });
            }
        }

        method.Body.OptimizeMacros();
    }