/// <summary> /// Replaces the <paramref name="oldInstruction"/> with a set of new instructions. /// </summary> /// <param name="oldInstruction">The instruction currently being evaluated.</param> /// <param name="hostMethod">The method that contains the target instruction.</param> /// <param name="IL">The CilWorker that will be used to emit the method body instructions.</param> protected override void Replace(Instruction oldInstruction, MethodDefinition hostMethod, CilWorker IL) { var targetField = (FieldReference) oldInstruction.Operand; var fieldType = targetField.FieldType; var isSetter = oldInstruction.OpCode == OpCodes.Stsfld || oldInstruction.OpCode == OpCodes.Stfld; if (isSetter) { hostMethod.Body.InitLocals = true; // Save the setter argument and box it if necessary if (fieldType.IsValueType || fieldType is GenericParameter) IL.Emit(OpCodes.Box, fieldType); IL.Emit(OpCodes.Stloc, _currentArgument); } // There's no need to push the current object instance // since the this pointer is pushed prior to the field call if (hostMethod.IsStatic) IL.Emit((OpCodes.Ldnull)); // Push the current method var module = hostMethod.DeclaringType.Module; // Push the current method onto the stack IL.PushMethod(hostMethod, module); // Push the current field onto the stack IL.PushField(targetField, module); // Push the host type onto the stack IL.PushType(hostMethod.DeclaringType, module); // Create the IFieldInterceptionContext instance IL.Emit(OpCodes.Newobj, _fieldContextCtor); IL.Emit(OpCodes.Stloc, _fieldContext); var skipInterception = IL.Create(OpCodes.Nop); // Obtain an interceptor instance if (hostMethod.IsStatic) { IL.Emit(OpCodes.Ldloc, _fieldContext); IL.Emit(OpCodes.Call, _getInterceptor); } else { IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType); IL.Emit(OpCodes.Brfalse, skipInterception); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType); IL.Emit(OpCodes.Callvirt, _getInstanceInterceptor); } // The field interceptor cannot be null IL.Emit(OpCodes.Stloc, _fieldInterceptor); IL.Emit(OpCodes.Ldloc, _fieldInterceptor); IL.Emit(OpCodes.Brfalse, skipInterception); // if (FieldInterceptor.CanIntercept(context) { IL.Emit(OpCodes.Ldloc, _fieldInterceptor); IL.Emit(OpCodes.Ldloc, _fieldContext); IL.Emit(OpCodes.Callvirt, _canIntercept); IL.Emit(OpCodes.Brfalse, skipInterception); var isGetter = oldInstruction.OpCode == OpCodes.Ldsfld || oldInstruction.OpCode == OpCodes.Ldfld; var endLabel = IL.Create(OpCodes.Nop); //Call the interceptor instead of the getter or setter if (isGetter) { IL.Emit(OpCodes.Ldloc, _fieldInterceptor); IL.Emit(OpCodes.Ldloc, _fieldContext); IL.Emit(OpCodes.Callvirt, _getValue); IL.Emit(OpCodes.Unbox_Any, fieldType); } if (isSetter) { // Push the 'this' pointer for instance field setters if (!hostMethod.IsStatic) IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldloc, _fieldInterceptor); IL.Emit(OpCodes.Ldloc, _fieldContext); IL.Emit(OpCodes.Ldloc, _currentArgument); // Unbox the setter value IL.Emit(OpCodes.Unbox_Any, fieldType); IL.Emit(OpCodes.Callvirt, _setValue); // Set the actual field value IL.Emit(OpCodes.Unbox_Any, fieldType); IL.Emit(oldInstruction.OpCode, targetField); } IL.Emit(OpCodes.Br, endLabel); // } IL.Append(skipInterception); // else { // Load the original field if (!hostMethod.IsStatic) IL.Emit(OpCodes.Ldarg_0); if (isSetter) { IL.Emit(OpCodes.Ldloc, _currentArgument); // Unbox the setter value IL.Emit(OpCodes.Unbox_Any, fieldType); } IL.Emit(oldInstruction.OpCode, targetField); // } IL.Append(endLabel); }