Пример #1
0
        private static void InjectParameterInputCheck(ParameterDefinition parameter, MethodDefinition method, ModuleReferences moduleReferences)
        {
            var instructions = method.Body.Instructions;
            var instruction0 = instructions[0];
            int index        = 0;

            instructions.Insert(index++, Instruction.Create(OpCodes.Ldarg, parameter));
            ILHelpers.InsertGetValueRef(ref index, instructions, parameter.ParameterType);
            ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                ref index, instructions, moduleReferences.ThrowArgumentNullMethod, parameter.Name, instruction0);
        }
        private static void InjectAsyncStateMachineCheck(MethodDefinition method, TypeDefinition stateMachineType, WeavingContext weavingContext)
        {
            var bclReferences    = weavingContext.BclReferences;
            var moduleReferences = weavingContext.ModuleReferences;

            if (!(GetMoveNextMethod(stateMachineType) is { } moveNextMethod))
            {
                WriteSkipWarning("'MoveNext' method not found.");
                return;
            }

            var moveNextMethodBody = moveNextMethod.Body;
            var instructions       = moveNextMethodBody.Instructions;

            if (!(GetSetResultMethodRef(instructions, out int index) is { } setResultMethodRef))
            {
                WriteSkipWarning("'SetResult' method not found.");
                return;
            }

            if (!(GetSetExceptionMethodRef(setResultMethodRef.DeclaringType, bclReferences) is { } setExceptionMethodRef))
            {
                WriteSkipWarning("'SetException' method not found.");
                return;
            }

            var resultType = ((GenericInstanceType)setResultMethodRef.DeclaringType).GenericArguments[0];

            moveNextMethodBody.SimplifyMacros(); // For reliability just in case codegen is different and branches into this section of instructions exists

            // Keep instruction that loads result value where is it so that branches stay pointed to it but insert another one that stays with the SetResult call.

            var oldLoadResultInstruction = instructions[--index];
            var newLoadResultInstruction = oldLoadResultInstruction.Clone();

            instructions.Insert(++index, newLoadResultInstruction); // Don't increment index so this stays with the SetResult call

            // Value on stack is the result value

            ILHelpers.InsertGetValueRef(ref index, instructions, resultType);

            const string message = "Async task result nullability contract was broken.";

            ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                ref index, instructions, moduleReferences.GetAsyncResultNullExceptionMethod, message, newLoadResultInstruction);

            instructions.Insert(index++, Instruction.Create(OpCodes.Call, setExceptionMethodRef));
            instructions.Insert(index++, Instruction.Create(OpCodes.Ret));

            moveNextMethodBody.OptimizeMacros();

            void WriteSkipWarning(string message) => weavingContext.WriteWarning($"Skipping async state machine result check on method '{method}': {message}");
        }
        private static void InjectIteratorStateMachineChecks(MethodDefinition method, TypeDefinition stateMachineType, WeavingContext weavingContext)
        {
            var moduleReferences = weavingContext.ModuleReferences;

            if (!(GetMoveNextMethod(stateMachineType) is { } moveNextMethod))
            {
                WriteSkipWarning("'MoveNext' method not found on state machine.");
                return;
            }

            if (!(GetFieldFromCurrentGetter(stateMachineType) is { } currentField))
            {
                WriteSkipWarning("'Current' getter with field reference not found.");
                return;
            }

            var moveNextMethodBody = moveNextMethod.Body;
            var instructions       = moveNextMethodBody.Instructions;

            moveNextMethodBody.SimplifyMacros();

            for (int index = 1; index < instructions.Count; index++)
            {
                var instruction = instructions[index];

                // Inject checks before all successful MoveNext operations.

                if (instruction.OpCode == OpCodes.Ret && instruction.Previous.OpCode == OpCodes.Ldc_I4 && (int)instruction.Previous.Operand == 1)
                {
                    index--;
                    instructions.Insert(index++, Instruction.Create(OpCodes.Ldarg_0));
                    instructions.Insert(index++, Instruction.Create(OpCodes.Ldfld, currentField));
                    ILHelpers.InsertGetValueRef(ref index, instructions, currentField.FieldType);

                    const string message = "Enumerator result nullability contract was broken.";
                    ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                        ref index, instructions, moduleReferences.ThrowOutputNullMethod, message, instructions[index]);
                    index += 2;
                }
            }

            moveNextMethodBody.OptimizeMacros();

            void WriteSkipWarning(string message) => weavingContext.WriteWarning($"Skipping iterator state machine result checks on method '{method}': {message}");
        }
Пример #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;
            }
        }
        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 InjectAsyncIteratorStateMachineChecks(MethodDefinition method, TypeDefinition stateMachineType, WeavingContext weavingContext)
        {
            var bclReferences    = weavingContext.BclReferences;
            var moduleReferences = weavingContext.ModuleReferences;

            if (!(GetMoveNextMethod(stateMachineType) is { } moveNextMethod))
            {
                WriteSkipWarning("'MoveNext' method not found on state machine.");
                return;
            }

            var moveNextMethodBody = moveNextMethod.Body;
            var instructions       = moveNextMethodBody.Instructions;

            if (!(GetSetResultMethodRef(instructions, out int index) is { } setResultMethodRef))
            {
                WriteSkipWarning("'SetResult' method not found.");
                return;
            }

            if (instructions[index - 1].OpCode != OpCodes.Ldc_I4_1)
            {
                WriteSkipWarning("Unexpected state machine implementation - 'SetResult' method argument was not true.");
                return;
            }

            if (!(GetSetExceptionMethodRef(setResultMethodRef.DeclaringType, bclReferences) is { } setExceptionMethodRef))
            {
                WriteSkipWarning("'SetException' method not found.");
                return;
            }

            if (!(GetFieldFromCurrentGetter(stateMachineType) is { } currentField))
            {
                WriteSkipWarning("'Current' getter with field reference not found.");
                return;
            }

            moveNextMethodBody.SimplifyMacros(); // For reliability just in case codegen is different and branches into this section of instructions exists

            // Go back one instruction to keep the argument for SetResult together with the call

            var oldBlockStart = instructions[--index];

            // Stack currently has the SetException/SetResult object on it

            instructions.Insert(index++, Instruction.Create(OpCodes.Ldarg_0));
            instructions.Insert(index++, Instruction.Create(OpCodes.Ldfld, currentField));
            ILHelpers.InsertGetValueRef(ref index, instructions, currentField.FieldType);

            const string message = "Async enumerator result nullability contract was broken.";

            ILHelpers.InsertThrowHelperCallIfValueRefIsNull(
                ref index, instructions, moduleReferences.GetAsyncResultNullExceptionMethod, message, oldBlockStart);

            instructions.Insert(index++, Instruction.Create(OpCodes.Call, setExceptionMethodRef));
            instructions.Insert(index++, Instruction.Create(OpCodes.Ret));

            moveNextMethodBody.SimplifyMacros();

            void WriteSkipWarning(string message) => weavingContext.WriteWarning($"Skipping async iterator state machine result checks on method '{method}': {message}");
        }