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}"); }
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; } }