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