private void SaveInvocationInfo(CilWorker IL, MethodReference targetMethod, ModuleDefinition module, TypeReference returnType) { // If the target method is an instance method, then the remaining item on the stack // will be the target object instance // Put all the method arguments into the argument stack foreach (ParameterReference param in targetMethod.Parameters) { // Save the current argument var parameterType = param.ParameterType; if (parameterType.IsValueType || parameterType is GenericParameter) IL.Emit(OpCodes.Box, parameterType); IL.Emit(OpCodes.Stloc, _currentArgument); IL.Emit(OpCodes.Ldloc, _currentArguments); IL.Emit(OpCodes.Ldloc, _currentArgument); IL.Emit(OpCodes.Callvirt, _pushMethod); } // Static methods will always have a null reference as the target if (!targetMethod.HasThis) IL.Emit(OpCodes.Ldnull); // Box the target, if necessary var declaringType = targetMethod.GetDeclaringType(); if (targetMethod.HasThis && (declaringType.IsValueType || declaringType is GenericParameter)) IL.Emit(OpCodes.Box, declaringType); // Save the target IL.Emit(OpCodes.Stloc, _target); IL.Emit(OpCodes.Ldloc, _target); // Push the current method IL.PushMethod(targetMethod, module); // Push the stack trace PushStackTrace(IL, module); var systemType = module.Import(typeof(Type)); // Save the parameter types var parameterCount = targetMethod.Parameters.Count; IL.Emit(OpCodes.Ldc_I4, parameterCount); IL.Emit(OpCodes.Newarr, systemType); IL.Emit(OpCodes.Stloc, _parameterTypes); IL.SaveParameterTypes(targetMethod, module, _parameterTypes); IL.Emit(OpCodes.Ldloc, _parameterTypes); // Save the type arguments var genericParameterCount = targetMethod.GenericParameters.Count; IL.Emit(OpCodes.Ldc_I4, genericParameterCount); IL.Emit(OpCodes.Newarr, systemType); IL.Emit(OpCodes.Stloc, _typeArguments); IL.PushGenericArguments(targetMethod, module, _typeArguments); IL.Emit(OpCodes.Ldloc, _typeArguments); // Push the return type IL.PushType(returnType, module); // Save the method arguments IL.Emit(OpCodes.Ldloc, _currentArguments); IL.Emit(OpCodes.Callvirt, _toArray); IL.Emit(OpCodes.Newobj, _invocationInfoCtor); IL.Emit(OpCodes.Stloc, _invocationInfo); }
/// <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); }