private static void InjectMethodChecks(MethodContext methodContext)
        {
            var method         = methodContext.Method;
            var weavingContext = methodContext.WeavingContext;
            var bclReferences  = weavingContext.BclReferences;

            bool methodNullChecksEnabled = methodContext.NullChecksEnabled;
            bool?returnNullChecksValue   = method.MethodReturnType.GetNullChecksAttributeValue(weavingContext, true);

            bool injectParamChecks  = methodNullChecksEnabled;
            bool injectReturnChecks = weavingContext.CheckOutputs &&
                                      (returnNullChecksValue == true || (methodNullChecksEnabled && returnNullChecksValue != false)) &&
                                      !method.ReturnType.IsRequiredModifier &&
                                      method.ReturnType.FullName != bclReferences.VoidType.FullName;

            if (!(injectParamChecks || injectReturnChecks) || !(methodContext.WeavingContext.CheckNonPublic || IsMethodPossiblyExposed(method)))
            {
                return;
            }

            ReturnBlockInfo returnBlockInfo = null;

            // InjectReturnChecks will simplify macros if it modifies the method. Parameter checks don't simplify because they don't change branch offsets.

            bool returnChecksInjected = injectReturnChecks && InjectReturnChecks(methodContext, ref returnBlockInfo);
            bool paramChecksInjected  = injectParamChecks && InjectParameterChecks(methodContext, ref returnBlockInfo);

            if (returnBlockInfo != null)
            {
                UpdateReturnBlockInstructionReferences(method, returnBlockInfo);
            }

            // Simplify injected macros and/or simplified macros if return checks simplified them.

            if (paramChecksInjected)
            {
                AddParamChecksToDebugInfo(method);
            }

            if (paramChecksInjected || returnChecksInjected)
            {
                method.Body.OptimizeMacros();
            }
        private static void InjectReturnValueCheck(MethodDefinition method, ReturnBlockInfo returnBlockInfo, ModuleReferences moduleReferences)
        {
            var instructions           = method.Body.Instructions;
            var returnBlockStartPoints = returnBlockInfo.NewStartPoints;

            for (int i = 0; i < returnBlockStartPoints.Length; i++)
            {
                Instruction injectionPoint = returnBlockStartPoints[i];
                int         index          = instructions.LastIndexOf(injectionPoint);

                // Value on stack is the return value
                var firstInstruction = Instruction.Create(OpCodes.Dup);
                instructions.Insert(index++, firstInstruction);
                ILHelpers.InsertGetValueRef(ref index, instructions, method.ReturnType);

                const string message = "Return value nullability contract was broken.";
                ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                    ref index, instructions, moduleReferences.ThrowOutputNullMethod, message, injectionPoint, returnBlockInfo);

                returnBlockStartPoints[i] = firstInstruction;
            }
        }
        private static bool InjectSynchronousTaskResultCheck(MethodDefinition method, ReturnBlockInfo returnBlockInfo, WeavingContext weavingContext)
        {
            var bclReferences    = weavingContext.BclReferences;
            var moduleReferences = weavingContext.ModuleReferences;

            var taskType         = (GenericInstanceType)method.ReturnType;
            var genericParameter = taskType.Resolve().GenericParameters[0];

            var isCompletedGetter = new MethodReference("get_IsCompleted", bclReferences.BoolType, taskType)
            {
                HasThis = true
            };
            var isCanceledGetter = new MethodReference("get_IsCanceled", bclReferences.BoolType, taskType)
            {
                HasThis = true
            };
            var isFaultedGetter = new MethodReference("get_IsFaulted", bclReferences.BoolType, taskType)
            {
                HasThis = true
            };
            var resultGetter = new MethodReference("get_Result", genericParameter, taskType)
            {
                HasThis = true
            };

            if (isCompletedGetter.Resolve() == null)
            {
                WriteSkipWarning("'IsComplete' getter not found.");
                return(false);
            }

            if (isCanceledGetter.Resolve() == null)
            {
                WriteSkipWarning("'IsCanceled' getter not found.");
                return(false);
            }

            if (isFaultedGetter.Resolve() == null)
            {
                WriteSkipWarning("'IsFaulted' getter not found.");
                return(false);
            }

            if (resultGetter.Resolve() == null)
            {
                WriteSkipWarning("'Result' getter not found.");
                return(false);
            }

            var resultType             = taskType.GenericArguments[0];
            var instructions           = method.Body.Instructions;
            var returnBlockStartPoints = returnBlockInfo.NewStartPoints;

            for (int i = 0; i < returnBlockStartPoints.Length; i++)
            {
                Instruction returnBlockStartPoint = returnBlockStartPoints[i];
                int         index = instructions.LastIndexOf(returnBlockStartPoint);

                // Value on stack is the return task

                var firstInstruction = Instruction.Create(OpCodes.Dup);
                instructions.Insert(index++, firstInstruction);
                instructions.Insert(index++, Instruction.Create(OpCodes.Call, isCompletedGetter));
                instructions.Insert(index++, returnBlockInfo.CreateBranchInstruction(OpCodes.Brfalse_S, returnBlockStartPoint));

                instructions.Insert(index++, Instruction.Create(OpCodes.Dup));
                instructions.Insert(index++, Instruction.Create(OpCodes.Call, isCanceledGetter));
                instructions.Insert(index++, returnBlockInfo.CreateBranchInstruction(OpCodes.Brtrue_S, returnBlockStartPoint));

                instructions.Insert(index++, Instruction.Create(OpCodes.Dup));
                instructions.Insert(index++, Instruction.Create(OpCodes.Call, isFaultedGetter));
                instructions.Insert(index++, returnBlockInfo.CreateBranchInstruction(OpCodes.Brtrue_S, returnBlockStartPoint));

                instructions.Insert(index++, Instruction.Create(OpCodes.Dup));
                instructions.Insert(index++, Instruction.Create(OpCodes.Call, resultGetter));
                ILHelpers.InsertGetValueRef(ref index, instructions, resultType);

                const string message = "Task result nullability contract was broken.";
                ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                    ref index, instructions, moduleReferences.ThrowOutputNullMethod, message, returnBlockStartPoint, returnBlockInfo);

                returnBlockStartPoints[i] = firstInstruction;
            }

            return(true);

            void WriteSkipWarning(string message) => weavingContext.WriteWarning($"Skipping synchronous task result check on method '{method}': {message}");
        }
Ejemplo n.º 4
0
        private static void InjectParameterOutputCheck(ParameterDefinition parameter, MethodDefinition method, ReturnBlockInfo returnBlockInfo, ModuleReferences moduleReferences)
        {
            var instructions           = method.Body.Instructions;
            var returnBlockStartPoints = returnBlockInfo.NewStartPoints;

            for (int i = 0; i < returnBlockStartPoints.Length; i++)
            {
                Instruction injectionPoint = returnBlockStartPoints[i];
                int         index          = instructions.LastIndexOf(injectionPoint);

                var firstInstruction = Instruction.Create(OpCodes.Ldarg, parameter);
                instructions.Insert(index++, firstInstruction);
                ILHelpers.InsertGetValueRef(ref index, instructions, parameter.ParameterType);

                string message = $"Output parameter '{parameter.Name}' nullability contract was broken.";
                ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                    ref index, instructions, moduleReferences.ThrowOutputNullMethod, message, injectionPoint, returnBlockInfo);

                returnBlockStartPoints[i] = firstInstruction;
            }
        }