/// <inheritdoc /> public bool RemoveInterceptor(MethodInfo method, IInterceptor interceptor) { if (method == null) { throw new ArgumentNullException(@"method"); } if (interceptor == null) { throw new ArgumentNullException(@"interceptor"); } return(StaticInterceptorStub.RemoveInterceptor(method, interceptor)); }
/// <inheritdoc /> public void AddInterceptor(MethodInfo method, IInterceptor interceptor) { if (method == null) { throw new ArgumentNullException(@"method"); } if (interceptor == null) { throw new ArgumentNullException(@"interceptor"); } StaticInterceptorStub.AddInterceptor(method, interceptor); }
/// <summary> /// Removes an interceptor. /// </summary> /// <param name="method">The method to intercept</param> /// <param name="interceptor">The interceptor to remove</param> /// <returns>True if an interceptor was removed</returns> internal static bool RemoveInterceptor(MethodInfo method, IInterceptor interceptor) { lock (syncRoot) { FieldInfo stubField = GetStubField(method); StaticInterceptorStub stub = (StaticInterceptorStub)stubField.GetValue(null); if (stub == null) { return(false); } IInterceptor[] oldInterceptors = stub.interceptors; int index = Array.IndexOf(oldInterceptors, interceptor); if (index < 0) { return(false); } if (oldInterceptors.Length == 1) { stub = null; } else { IInterceptor[] newInterceptors = new IInterceptor[oldInterceptors.Length - 1]; int remainder = newInterceptors.Length - index; if (index != 0) { Array.Copy(oldInterceptors, newInterceptors, index); } if (remainder != 0) { Array.Copy(oldInterceptors, index + 1, newInterceptors, index, remainder); } stub = new StaticInterceptorStub(method, newInterceptors, stub.methodInvocationTarget); } stubField.SetValue(null, stub); return(true); } }
/// <summary> /// Adds an interceptor. /// </summary> /// <param name="method">The method to intercept</param> /// <param name="interceptor">The interceptor to add</param> internal static void AddInterceptor(MethodInfo method, IInterceptor interceptor) { lock (syncRoot) { FieldInfo stubField = GetStubField(method); StaticInterceptorStub stub = (StaticInterceptorStub)stubField.GetValue(null); IInterceptor[] newInterceptors; if (stub == null) { newInterceptors = new IInterceptor[] { interceptor }; stub = new StaticInterceptorStub(method, newInterceptors); } else { newInterceptors = stub.interceptors; Array.Resize(ref newInterceptors, newInterceptors.Length + 1); newInterceptors[newInterceptors.Length - 1] = interceptor; stub = new StaticInterceptorStub(method, newInterceptors, stub.methodInvocationTarget); } stubField.SetValue(null, stub); } }
/// <summary> /// Creates an invocation. /// </summary> /// <param name="interceptorStub">The stub</param> /// <param name="invocationTarget">The invocation target or null if intercepting a static method</param> /// <param name="arguments">The arguments</param> public StaticInvocation(StaticInterceptorStub interceptorStub, object invocationTarget, object[] arguments) { this.interceptorStub = interceptorStub; this.invocationTarget = invocationTarget; this.arguments = arguments; }
/// <summary> /// Removes an interceptor. /// </summary> /// <param name="method">The method to intercept</param> /// <param name="interceptor">The interceptor to remove</param> /// <returns>True if an interceptor was removed</returns> internal static bool RemoveInterceptor(MethodInfo method, IInterceptor interceptor) { lock (syncRoot) { FieldInfo stubField = GetStubField(method); StaticInterceptorStub stub = (StaticInterceptorStub)stubField.GetValue(null); if (stub == null) return false; IInterceptor[] oldInterceptors = stub.interceptors; int index = Array.IndexOf(oldInterceptors, interceptor); if (index < 0) return false; if (oldInterceptors.Length == 1) { stub = null; } else { IInterceptor[] newInterceptors = new IInterceptor[oldInterceptors.Length - 1]; int remainder = newInterceptors.Length - index; if (index != 0) Array.Copy(oldInterceptors, newInterceptors, index); if (remainder != 0) Array.Copy(oldInterceptors, index + 1, newInterceptors, index, remainder); stub = new StaticInterceptorStub(method, newInterceptors, stub.methodInvocationTarget); } stubField.SetValue(null, stub); return true; } }
private void Rewrite(TypeDefinition typeDefinition, MethodDefinition methodDefinition, List <MethodDefinition> addedMethods) { if (!methodDefinition.HasBody) { return; } ImportReferences(); // Create an interceptor stub for the method. string stubFieldName = StaticInterceptorStub.GetStubFieldName(methodDefinition.Name); FieldDefinition stubFieldDefinition = new FieldDefinition(stubFieldName, staticInterceptorStubReference, FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.NotSerialized); typeDefinition.Fields.Add(stubFieldDefinition); // Clone the original method and give it a new name. MethodDefinition targetMethodDefinition = methodDefinition.Clone(); targetMethodDefinition.Overrides.Clear(); targetMethodDefinition.Attributes = (methodDefinition.Attributes & MethodAttributes.Static) | MethodAttributes.Private | MethodAttributes.HideBySig; targetMethodDefinition.CallingConvention = MethodCallingConvention.Default; targetMethodDefinition.Name = StaticInterceptorStub.GetTargetMethodName(methodDefinition.Name); addedMethods.Add(targetMethodDefinition); // Replace the original method with a stub that calls the callback. MethodBody body = new MethodBody(methodDefinition); body.InitLocals = true; methodDefinition.Body = body; CilWorker worker = body.CilWorker; /*** Obtain the invocation, if needed ***/ // Load the stub if it has been initialized. VariableDefinition stubVariableDefinition = new VariableDefinition(staticInterceptorStubReference); body.Variables.Add(stubVariableDefinition); worker.Emit(OpCodes.Ldsfld, stubFieldDefinition); worker.Emit(OpCodes.Dup); worker.Emit(OpCodes.Stloc, stubVariableDefinition); Instruction fastPathEntryBranch = worker.Emit(OpCodes.Brfalse, body.Instructions.Outside); /*** Slow path ***/ // Copy arguments to an array. VariableDefinition argumentsVariableDefinition = null; if (methodDefinition.Parameters.Count != 0) { argumentsVariableDefinition = new VariableDefinition(objectArrayReference); body.Variables.Add(argumentsVariableDefinition); worker.Emit(OpCodes.Ldc_I4, methodDefinition.Parameters.Count); worker.Emit(OpCodes.Newarr, objectReference); worker.Emit(OpCodes.Stloc, argumentsVariableDefinition); foreach (ParameterDefinition param in methodDefinition.Parameters) { if ((param.Attributes & ParameterAttributes.In) != 0) { worker.Emit(OpCodes.Ldloc, argumentsVariableDefinition); worker.Emit(OpCodes.Ldc_I4, param.Sequence); worker.Emit(OpCodes.Ldarg, param); worker.Emit(OpCodes.Box, objectReference); worker.Emit(OpCodes.Stelem_Ref); } } } // Create the invocation. worker.Emit(OpCodes.Ldloc, stubVariableDefinition); if (methodDefinition.HasThis) { worker.Emit(OpCodes.Ldarg_0); } else { worker.Emit(OpCodes.Ldnull); } if (argumentsVariableDefinition == null) { worker.Emit(OpCodes.Ldsfld, noArgumentsReference); } else { worker.Emit(OpCodes.Ldloc, argumentsVariableDefinition); } worker.Emit(OpCodes.Newobj, staticInvocationConstructorReference); // Execute it (leaves the result on the stack). worker.Emit(OpCodes.Call, executeReference); // Copy any ref and out arguments back out of the invocation's array. if (argumentsVariableDefinition != null) { foreach (ParameterDefinition param in methodDefinition.Parameters) { if ((param.Attributes & ParameterAttributes.Out) != 0) { worker.Emit(OpCodes.Ldloc, argumentsVariableDefinition); worker.Emit(OpCodes.Ldc_I4, param.Sequence); worker.Emit(OpCodes.Ldelem_Ref); worker.Emit(OpCodes.Unbox_Any, param.ParameterType); worker.Emit(OpCodes.Starg, param); } } } // Unbox the result if needed and return. if (CecilUtils.IsVoid(methodDefinition.ReturnType.ReturnType)) { worker.Emit(OpCodes.Pop); } else { worker.Emit(OpCodes.Unbox_Any, methodDefinition.ReturnType.ReturnType); } Instruction slowPathReturnBranch = worker.Emit(OpCodes.Br, body.Instructions.Outside); /*** Fast path ***/ // Load up all arguments. Instruction fastPathEntryInstr = null; if (methodDefinition.HasThis) { fastPathEntryInstr = worker.Emit(OpCodes.Ldarg, methodDefinition.This); } foreach (ParameterDefinition param in methodDefinition.Parameters) { Instruction instr = worker.Emit(OpCodes.Ldarg, param); if (fastPathEntryInstr == null) { fastPathEntryInstr = instr; } } // Emit a tail call back to the original method for the fast-path without an invocation. worker.Emit(OpCodes.Tail); worker.Emit(OpCodes.Call, targetMethodDefinition); /*** Common return ***/ Instruction returnInstr = worker.Emit(OpCodes.Ret); // Patch branches. fastPathEntryBranch.Operand = fastPathEntryInstr; slowPathReturnBranch.Operand = returnInstr; }