예제 #1
0
            protected override void ImplementBody(ILProcessor il, FieldReference methodInfoField, FieldReference propertyInfoField, MethodReference proceed, MethodReference proceedTargetMethod)
            {
                // If it's abstract, then the method is entirely implemented by the InvocationHandler
                if (Method.IsAbstract)
                {
                    base.ImplementBody(il, methodInfoField, propertyInfoField, proceed, proceedTargetMethod);
                }
                // Otherwise, it is implemented by the class itself, and calling this.Invocation().Proceed() calls the InvocationHandler
                else
                {
                    Method.Body.InitLocals = true;

                    // First declare the invocation in a private local variable
                    var invocation   = new VariableDefinition(ClassWeaver.Context.InvocationType);
                    var instructions = Method.Body.Instructions.ToList();
                    Method.Body.Instructions.Clear();                                // Wipe out the existing instructions for the method
                    Method.Body.Variables.Add(invocation);                           // Add a variable to store the invocation
                    EmitInvocation(il, methodInfoField, propertyInfoField, proceed); // Put the invocation on the stack
                    il.Emit(OpCodes.Dup);                                            // Duplicate invocation for below
                    il.Emit(OpCodes.Stloc, invocation);                              // Store the invocation in the variable declared (just) earlier

                    // Add the invocation to the end of the array
                    il.Emit(OpCodes.Call, ClassWeaver.Context.InvocationGetArguments);                   // Array now on the stack with the invocation above it
                    il.Emit(OpCodes.Ldc_I4, Method.Parameters.Count);                                    // Array index
                    il.Emit(OpCodes.Ldloc, invocation);                                                  // Element value
                    il.Emit(OpCodes.Stelem_Any, ClassWeaver.Context.ModuleDefinition.TypeSystem.Object); // Set array at index to element value

                    // Special instrumentation for async methods
                    var returnType = Method.ReturnType;
                    if (ClassWeaver.Context.TaskType.IsAssignableFrom(returnType))
                    {
                        // If the return type is Task<T>
                        if (returnType.IsTaskT())
                        {
                            var actualReturnType     = returnType.GetTaskType();
                            var expectedAsyncBuilder = ClassWeaver.Context.AsyncTaskMethodBuilder.MakeGenericInstanceType(actualReturnType);

                            // Now find the call to .Start() (only will be found if we're async)
                            var startInstructionMethod = (GenericInstanceMethod)instructions
                                                         .Where(x => x.OpCode == OpCodes.Call)
                                                         .Select(x => (MethodReference)x.Operand)
                                                         .Where(x => x.IsGenericInstance && x.Name == "Start" && x.DeclaringType.CompareTo(expectedAsyncBuilder))
                                                         .SingleOrDefault();
                            if (startInstructionMethod != null)
                            {
                                var asyncType       = startInstructionMethod.GenericArguments[0];
                                var invocationField = InstrumentAsyncType(asyncType);

                                // Find the first instruction that is setting a field on the async type.  We can just assume it's a three instruction
                                // set (it always is in this context)  And we add our instructions to set the invocation field.
                                var nextSetFieldIndex = instructions.IndexOf(x => x.OpCode == OpCodes.Stfld && x.Operand is FieldDefinition && ((FieldDefinition)x.Operand).DeclaringType.CompareTo(asyncType));
                                if (nextSetFieldIndex == -1)
                                {
                                    throw new Exception($"Could not find expected stfld of async type: {asyncType}");
                                }
                                var setFieldLoadInstance = instructions[nextSetFieldIndex - 2];
                                instructions.Insert(nextSetFieldIndex - 2, il.Clone(setFieldLoadInstance));
                                instructions.Insert(nextSetFieldIndex - 1, il.Create(OpCodes.Ldloc, invocation));
                                instructions.Insert(nextSetFieldIndex, il.Create(OpCodes.Stfld, invocationField));
                            }
                        }
                    }

                    InstrumentInstructions(il, instructions, 2, x => x.Emit(OpCodes.Ldloc, invocation));
                }
            }