/// <summary> /// Saves the return value from a given method call. /// </summary> /// <param name="IL">The <see cref="CilWorker"/> pointing to the target method body.</param> public void Emit(CilWorker IL) { var module = IL.GetModule(); var voidType = module.ImportType(typeof (void)); var returnTypeIsValueType = _returnType != voidType && _returnType.IsValueType; if (_returnType is GenericParameter || returnTypeIsValueType) IL.Create(OpCodes.Box, _returnType); if (_returnType != voidType) IL.Create(OpCodes.Stloc, _returnValue); }
/// <summary> /// Saves the return value from a given method call. /// </summary> /// <param name="IL">The <see cref="CilWorker"/> pointing to the target method body.</param> public void Emit(CilWorker IL) { ModuleDefinition module = IL.GetModule(); TypeReference voidType = module.ImportType(typeof (void)); bool returnTypeIsValueType = _returnType != voidType && _returnType.IsValueType; if (_returnType is GenericParameter || returnTypeIsValueType) IL.Create(OpCodes.Box, _returnType); if (_returnType != voidType) IL.Create(OpCodes.Stloc, _returnValue); }
public void Emit(CilWorker IL) { var module = IL.GetModule(); var modifiableType = module.ImportType<IModifiableType>(); var getInterceptionDisabledMethod = module.ImportMethod<IModifiableType>("get_IsInterceptionDisabled"); if (!_hostMethod.HasThis) { IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Stloc, _interceptionDisabled); return; } var skipLabel = IL.Create(OpCodes.Nop); // var interceptionDisabled = this.IsInterceptionDisabled; IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, modifiableType); IL.Emit(OpCodes.Brfalse, skipLabel); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, modifiableType); IL.Emit(OpCodes.Callvirt, getInterceptionDisabledMethod); IL.Emit(OpCodes.Stloc, _interceptionDisabled); IL.Append(skipLabel); }
/// <summary> /// Adds method body interception to the target method. /// </summary> /// <param name="IL">The <see cref="CilWorker"/> pointing to the target method body.</param> public void Emit(CilWorker IL) { MethodDefinition method = IL.GetMethod(); TypeReference returnType = method.ReturnType.ReturnType; Instruction endLabel = IL.Create(OpCodes.Nop); Instruction executeOriginalInstructions = IL.Create(OpCodes.Nop); // Execute the method body replacement if and only if // interception is enabled IL.Emit(OpCodes.Ldloc, _interceptionDisabled); IL.Emit(OpCodes.Brtrue, executeOriginalInstructions); Instruction invokeReplacement = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, _methodReplacementProvider); IL.Emit(OpCodes.Brtrue, invokeReplacement); IL.Emit(OpCodes.Ldloc, _classMethodReplacementProvider); IL.Emit(OpCodes.Brtrue, invokeReplacement); IL.Emit(OpCodes.Br, executeOriginalInstructions); IL.Append(invokeReplacement); // This is equivalent to the following code: // var replacement = provider.GetMethodReplacement(info); var invokeMethodReplacement = new InvokeMethodReplacement(executeOriginalInstructions, _methodReplacementProvider, _classMethodReplacementProvider, _invocationInfo); invokeMethodReplacement.Emit(IL); IL.Emit(OpCodes.Br, endLabel); #region The original instruction block IL.Append(executeOriginalInstructions); var addOriginalInstructions = new AddOriginalInstructions(_oldInstructions, endLabel); addOriginalInstructions.Emit(IL); #endregion // Mark the end of the method body IL.Append(endLabel); var saveReturnValue = new SaveReturnValue(returnType, _returnValue); saveReturnValue.Emit(IL); }
public void Emit(CilWorker IL) { var targetMethod = IL.GetMethod(); var declaringType = targetMethod.DeclaringType; var module = declaringType.Module; var getSurroundingClassImplementation = new GetSurroundingClassImplementation(_invocationInfo, _surroundingClassImplementation, _registryType.GetMethod("GetSurroundingImplementation")); // var classAroundInvoke = AroundInvokeRegistry.GetSurroundingImplementation(info); getSurroundingClassImplementation.Emit(IL); // classAroundInvoke.BeforeInvoke(info); var skipInvoke = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, _surroundingClassImplementation); IL.Emit(OpCodes.Brfalse, skipInvoke); var beforeInvoke = module.ImportMethod<IBeforeInvoke>("BeforeInvoke"); // surroundingImplementation.BeforeInvoke(invocationInfo); IL.Emit(OpCodes.Ldloc, _surroundingClassImplementation); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Callvirt, beforeInvoke); IL.Append(skipInvoke); // if (surroundingImplementation != null) { if (!targetMethod.HasThis) return; var skipInvoke1 = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, _surroundingImplementation); IL.Emit(OpCodes.Brfalse, skipInvoke1); var beforeInvoke1 = module.ImportMethod<IBeforeInvoke>("BeforeInvoke"); // surroundingImplementation.BeforeInvoke(invocationInfo); IL.Emit(OpCodes.Ldloc, _surroundingImplementation); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Callvirt, beforeInvoke1); IL.Append(skipInvoke1); // } }
public void EmitNewObject(MethodDefinition hostMethod, CilWorker IL, MethodReference targetConstructor, TypeReference concreteType) { ParameterDefinitionCollection parameters = targetConstructor.Parameters; Instruction skipInterception = IL.Create(OpCodes.Nop); SaveConstructorArguments(IL, parameters); EmitCreateMethodActivationContext(hostMethod, IL, concreteType); // Skip the interception if an activator cannot be found EmitGetActivator(hostMethod, IL, skipInterception); IL.Emit(OpCodes.Stloc, _currentActivator); IL.Emit(OpCodes.Ldloc, _currentActivator); IL.Emit(OpCodes.Brfalse, skipInterception); // Determine if the activator can instantiate the method from the // current context IL.Emit(OpCodes.Ldloc, _currentActivator); IL.Emit(OpCodes.Ldloc, _methodContext); IL.Emit(OpCodes.Callvirt, _canActivate); IL.Emit(OpCodes.Brfalse, skipInterception); // Use the activator to create the object instance EmitCreateInstance(IL); // } Instruction endCreate = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Br, endCreate); // else { IL.Append(skipInterception); // Restore the arguments that were popped off the stack // by the list of constructor arguments int parameterCount = parameters.Count; for (int index = 0; index < parameterCount; index++) { ParameterDefinition currentParameter = parameters[index]; IL.Emit(OpCodes.Ldloc, _constructorArguments); IL.Emit(OpCodes.Ldc_I4, index); IL.Emit(OpCodes.Callvirt, _getItem); IL.Emit(OpCodes.Unbox_Any, currentParameter.ParameterType); } IL.Emit(OpCodes.Newobj, targetConstructor); // } IL.Append(endCreate); }
private static void Emit(CilWorker IL, ModuleDefinition module, VariableDefinition surroundingImplementation, VariableDefinition invocationInfo, VariableDefinition returnValue) { Instruction skipInvoke = IL.Create(OpCodes.Nop); Instruction skipPrint = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, surroundingImplementation); IL.Emit(OpCodes.Brtrue, skipPrint); IL.Append(skipPrint); IL.Emit(OpCodes.Ldloc, surroundingImplementation); IL.Emit(OpCodes.Brfalse, skipInvoke); MethodReference aroundInvoke = module.ImportMethod<IAfterInvoke>("AfterInvoke"); IL.Emit(OpCodes.Ldloc, surroundingImplementation); IL.Emit(OpCodes.Ldloc, invocationInfo); IL.Emit(OpCodes.Ldloc, returnValue); IL.Emit(OpCodes.Callvirt, aroundInvoke); IL.Append(skipInvoke); }
private static void GetMethodReplacementInstance(MethodDefinition method, CilWorker IL, VariableDefinition methodReplacement, VariableDefinition methodReplacementProvider, VariableDefinition invocationInfo) { var declaringType = method.DeclaringType; var module = declaringType.Module; var pushInstance = method.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull); var getReplacement = module.ImportMethod<IMethodReplacementProvider>("GetMethodReplacement"); IL.Emit(OpCodes.Ldloc, methodReplacementProvider); var skipGetMethodReplacement = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Brfalse, skipGetMethodReplacement); IL.Emit(OpCodes.Ldloc, methodReplacementProvider); IL.Append(pushInstance); IL.Emit(OpCodes.Ldloc, invocationInfo); IL.Emit(OpCodes.Callvirt, getReplacement); IL.Emit(OpCodes.Stloc, methodReplacement); IL.Append(skipGetMethodReplacement); }
public void AddEpilog(CilWorker IL) { var skipEpilog = IL.Create(OpCodes.Nop); // if (!IsInterceptionDisabled && surroundingImplementation != null) { IL.Emit(OpCodes.Ldloc, _interceptionDisabled); IL.Emit(OpCodes.Brtrue, skipEpilog); // surroundingImplementation.AfterInvoke(invocationInfo, returnValue); var emitAfterInvoke = new EmitAfterInvoke(_surroundingImplementation, _surroundingClassImplementation, _invocationInfo, _returnValue); emitAfterInvoke.Emit(IL); // } IL.Append(skipEpilog); }
public void Rewrite(MethodDefinition method, CilWorker IL, IEnumerable<Instruction> oldInstructions) { var targetMethod = _parameters.TargetMethod; var worker = targetMethod.GetILGenerator(); var module = worker.GetModule(); _getInterceptionDisabled.Emit(worker); // Construct the InvocationInfo instance var skipInvocationInfo = worker.Create(OpCodes.Nop); worker.Emit(OpCodes.Ldloc, _parameters.InterceptionDisabled); worker.Emit(OpCodes.Brtrue, skipInvocationInfo); var interceptedMethod = targetMethod; _emitter.Emit(targetMethod, interceptedMethod, _parameters.InvocationInfo); var skipGetReplacementProvider = IL.Create(OpCodes.Nop); // var provider = this.MethodReplacementProvider; //IL.Emit(OpCodes.Ldloc, _interceptionDisabled); //IL.Emit(OpCodes.Brtrue, skipGetReplacementProvider); _getInstanceMethodReplacementProvider.Emit(IL); _surroundMethodBody.AddProlog(worker); IL.Append(skipGetReplacementProvider); worker.Append(skipInvocationInfo); _getClassMethodReplacementProvider.Emit(worker); var returnType = targetMethod.ReturnType.ReturnType; _addMethodReplacement.Emit(worker); // Save the return value TypeReference voidType = module.Import(typeof(void)); _surroundMethodBody.AddEpilog(worker); if (returnType != voidType) worker.Emit(OpCodes.Ldloc, _parameters.ReturnValue); worker.Emit(OpCodes.Ret); }
public void Emit(CilWorker IL) { var module = IL.GetModule(); var method = IL.GetMethod(); var returnType = method.ReturnType.ReturnType; var methodReplacement = MethodDefinitionExtensions.AddLocal(method, typeof(IInterceptor)); GetMethodReplacementInstance(method, IL, methodReplacement, _methodReplacementProvider, _invocationInfo); var skipGetClassMethodReplacement = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, methodReplacement); IL.Emit(OpCodes.Brtrue, skipGetClassMethodReplacement); GetMethodReplacementInstance(method, IL, methodReplacement, _classMethodReplacementProvider, _invocationInfo); IL.Append(skipGetClassMethodReplacement); IL.Emit(OpCodes.Ldloc, methodReplacement); IL.Emit(OpCodes.Brfalse, _executeOriginalInstructions); // var returnValue = replacement.Intercept(info); InvokeInterceptor(module, IL, methodReplacement, returnType, _invocationInfo); }
private void EnteringMethodInstruction(MethodDefinition method, CilWorker worker) { MethodReference enteringMethod = method.DeclaringType.Module.Import(typeof (Profiler).GetMethod("EnteringMethod")); worker.InsertBefore(method.Body.Instructions[0], worker.Create(OpCodes.Call, enteringMethod)); worker.InsertBefore(method.Body.Instructions[0], worker.Create(OpCodes.Ldstr, MethodName(method))); }
private void InstrumentMethodAtThrow(CilWorker worker, Instruction throwInstruction, MethodReference profilerMethod) { Instruction newThrowInstruction = worker.Create(throwInstruction.OpCode); throwInstruction.OpCode = OpCodes.Call; throwInstruction.Operand = profilerMethod; worker.InsertAfter(throwInstruction, newThrowInstruction); }
private void Replace(CilWorker IL, Instruction oldInstruction, MethodReference targetMethod, MethodDefinition hostMethod, Instruction endLabel, Instruction callOriginalMethod) { var returnType = targetMethod.ReturnType.ReturnType; var module = hostMethod.DeclaringType.Module; if (!hostMethod.IsStatic) GetInstanceProvider(IL); var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull); // If all else fails, use the static method replacement provider IL.Append(pushInstance); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Call, _getStaticProvider); IL.Emit(OpCodes.Stloc, _staticProvider); var restoreArgumentStack = IL.Create(OpCodes.Nop); var callReplacement = IL.Create(OpCodes.Nop); var useStaticProvider = IL.Create(OpCodes.Nop); #region Use the instance method replacement provider IL.Emit(OpCodes.Ldloc, _instanceProvider); IL.Emit(OpCodes.Brfalse, useStaticProvider); EmitCanReplace(IL, hostMethod, _instanceProvider); IL.Emit(OpCodes.Ldloc, _canReplaceFlag); IL.Emit(OpCodes.Brfalse, useStaticProvider); EmitGetMethodReplacement(IL, hostMethod, _instanceProvider); IL.Emit(OpCodes.Ldloc, _replacement); IL.Emit(OpCodes.Brtrue, callReplacement); #endregion IL.Append(useStaticProvider); // if (!MethodReplacementProvider.CanReplace(info)) // CallOriginalMethod(); EmitCanReplace(IL, hostMethod, _staticProvider); IL.Emit(OpCodes.Ldloc, _canReplaceFlag); IL.Emit(OpCodes.Brfalse, restoreArgumentStack); EmitGetMethodReplacement(IL, hostMethod, _staticProvider); IL.Append(callReplacement); // if (replacement == null) // CallOriginalMethod(); IL.Emit(OpCodes.Ldloc, _replacement); IL.Emit(OpCodes.Brfalse, restoreArgumentStack); EmitInterceptorCall(IL); IL.PackageReturnValue(module, returnType); IL.Emit(OpCodes.Br, endLabel); IL.Append(restoreArgumentStack); // Reconstruct the method arguments if the interceptor // cannot be found // Push the target instance ReconstructMethodArguments(IL, targetMethod); // Mark the CallOriginalMethod instruction label IL.Append(callOriginalMethod); // Call the original method IL.Append(oldInstruction); }
private void EmitGetMethodReplacement(CilWorker IL, IMethodSignature hostMethod, VariableDefinition provider) { // var replacement = MethodReplacementProvider.GetReplacement(info); IL.Emit(OpCodes.Ldloc, provider); // Push the host instance var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull); IL.Append(pushInstance); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Callvirt, _getReplacement); IL.Emit(OpCodes.Stloc, _replacement); }
protected override void Replace(Instruction oldInstruction, MethodDefinition hostMethod, CilWorker IL) { var targetMethod = (MethodReference)oldInstruction.Operand; var callOriginalMethod = IL.Create(OpCodes.Nop); var returnType = targetMethod.ReturnType.ReturnType; var endLabel = IL.Create(OpCodes.Nop); var module = hostMethod.DeclaringType.Module; // Create the stack that will hold the method arguments IL.Emit(OpCodes.Newobj, _stackCtor); IL.Emit(OpCodes.Stloc, _currentArguments); SaveInvocationInfo(IL, targetMethod, module, returnType); var getInterceptionDisabled = new GetInterceptionDisabled(hostMethod, _interceptionDisabled); getInterceptionDisabled.Emit(IL); var surroundMethodBody = new SurroundMethodBody(_methodReplacementProvider, _aroundInvokeProvider, _invocationInfo, _interceptionDisabled, _returnValue, typeof(AroundInvokeMethodCallRegistry)); surroundMethodBody.AddProlog(IL); // Use the MethodReplacementProvider attached to the // current host instance Replace(IL, oldInstruction, targetMethod, hostMethod, endLabel, callOriginalMethod); IL.Append(endLabel); surroundMethodBody.AddEpilog(IL); }
private void EmitCreateMethodActivationContext(MethodDefinition method, CilWorker IL, TypeReference concreteType) { // TODO: Add static method support var pushThis = method.IsStatic ? IL.Create(OpCodes.Ldnull) : IL.Create(OpCodes.Ldarg_0); // Push the 'this' pointer onto the stack IL.Append(pushThis); var module = method.DeclaringType.Module; // Push the current method onto the stack IL.PushMethod(method, module); // Push the concrete type onto the stack IL.Emit(OpCodes.Ldtoken, concreteType); IL.Emit(OpCodes.Call, _getTypeFromHandle); IL.Emit(OpCodes.Ldloc, _constructorArguments); IL.Emit(OpCodes.Callvirt, _toArrayMethod); IL.Emit(OpCodes.Newobj, _methodActivationContextCtor); // var context = new MethodActivationContext(this, currentMethod, concreteType, args); IL.Emit(OpCodes.Stloc, _methodContext); }
private static void InsertCustomCodeCall( MethodDefinition method, MethodReference generatedTypeCtorRef, MethodReference generatedMethodRef, CilWorker cilWorker, Instruction instructionToInsertBefore, bool replaceMethod ) { bool hasReturnValue = (method.ReturnType.ReturnType.FullName != typeof(void).FullName); Instruction nop = cilWorker.Create(OpCodes.Nop); cilWorker.InsertBefore(instructionToInsertBefore, nop); // call ctor Instruction newGeneratedType = cilWorker.Create(OpCodes.Newobj, generatedTypeCtorRef); cilWorker.InsertBefore(instructionToInsertBefore, newGeneratedType); // load arguments on stack if any for (int i = 0; i < method.Parameters.Count; i++) { Instruction loadArg = cilWorker.Create(OpCodes.Ldarga_S, method.Parameters[i]); cilWorker.InsertBefore(instructionToInsertBefore, loadArg); } // call replacing method Instruction callGeneratedMethod = cilWorker.Create(OpCodes.Call, generatedMethodRef); cilWorker.InsertBefore(instructionToInsertBefore, callGeneratedMethod); if (hasReturnValue) { if (replaceMethod) { // add variable to list var generatedValueVar = new VariableDefinition(method.ReturnType.ReturnType); method.Body.Variables.Add(generatedValueVar); // assign to variable Instruction assignNewGeneratedValue = cilWorker.Create(OpCodes.Stloc, generatedValueVar); cilWorker.InsertBefore(instructionToInsertBefore, assignNewGeneratedValue); Instruction ldLoc = cilWorker.Create(OpCodes.Ldloc_0); Instruction brs = cilWorker.Create(OpCodes.Br_S, ldLoc); cilWorker.InsertBefore(instructionToInsertBefore, brs); cilWorker.InsertBefore(instructionToInsertBefore, ldLoc); } else { // remove value from stack Instruction pop = cilWorker.Create(OpCodes.Pop); cilWorker.InsertBefore(instructionToInsertBefore, pop); } } }
private static void DebugLine(AssemblyDefinition assembly, CilWorker il, string text, params Instruction[] appendInstructions) { il.Append(il.Create(OpCodes.Ldstr, text)); //Append any instructions we need to if (appendInstructions.Length > 0) { var concat = assembly.Import(typeof (String).GetMethod("Concat", new[] {typeof (string), typeof (string)})); foreach (Instruction i in appendInstructions) { il.Append(il.Create(OpCodes.Ldstr, ",")); il.Append(il.Create(OpCodes.Call, concat)); il.Append(i); il.Append(il.Create(OpCodes.Call, concat)); } } var writeLine = assembly.Import(typeof (Console).GetMethod("WriteLine", new[] {typeof (string)})); il.Append(il.Create(OpCodes.Call, writeLine)); }
private void EmitCanReplace(CilWorker IL, IMethodSignature hostMethod, VariableDefinition provider) { var skipGetProvider = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, provider); IL.Emit(OpCodes.Brfalse, skipGetProvider); IL.Emit(OpCodes.Ldloc, provider); // Push the host instance var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull); IL.Append(pushInstance); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Callvirt, _canReplace); IL.Emit(OpCodes.Stloc, _canReplaceFlag); IL.Append(skipGetProvider); }
/// <summary> /// Adds a prolog to the given method body. /// </summary> /// <param name="IL">The <see cref="CilWorker"/> that points to the given method body.</param> public void AddProlog(CilWorker IL) { var method = IL.GetMethod(); _surroundingImplementation = method.AddLocal<IAroundInvoke>(); _surroundingClassImplementation = method.AddLocal<IAroundInvoke>(); var skipProlog = IL.Create(OpCodes.Nop); var declaringType = method.DeclaringType; var module = declaringType.Module; var modifiableType = module.ImportType<IModifiableType>(); if (method.HasThis) { IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, modifiableType); IL.Emit(OpCodes.Brfalse, skipProlog); } IL.Emit(OpCodes.Ldloc, _interceptionDisabled); IL.Emit(OpCodes.Brtrue, skipProlog); // var provider = this.MethodReplacementProvider; if (_getMethodReplacementProvider != null) _getMethodReplacementProvider.Emit(IL); var getAroundInvokeProvider = new GetAroundInvokeProvider(_aroundInvokeProvider, _providerName); getAroundInvokeProvider.Emit(IL); // if (aroundInvokeProvider != null ) { var skipGetSurroundingImplementation = IL.Create(OpCodes.Nop); var getSurroundingImplementationInstance = new GetSurroundingImplementationInstance(_aroundInvokeProvider, _invocationInfo, _surroundingImplementation, skipGetSurroundingImplementation); getSurroundingImplementationInstance.Emit(IL); // } IL.Append(skipGetSurroundingImplementation); var emitBeforeInvoke = new EmitBeforeInvoke(_invocationInfo, _surroundingClassImplementation, _surroundingImplementation, _registryType); emitBeforeInvoke.Emit(IL); IL.Append(skipProlog); }
private void GetInstanceProvider(CilWorker IL) { var skipInstanceProvider = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, _hostInterfaceType); IL.Emit(OpCodes.Brfalse, skipInstanceProvider); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Isinst, _hostInterfaceType); IL.Emit(OpCodes.Callvirt, _getProvider); IL.Emit(OpCodes.Stloc, _instanceProvider); IL.Emit(OpCodes.Ldloc, _instanceProvider); IL.Emit(OpCodes.Brtrue, skipInstanceProvider); IL.Append(skipInstanceProvider); }
/// <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); }
/// <summary> /// Rewrites the instructions in the target method body to support dynamic exception handling. /// </summary> /// <param name="targetMethod">The target method.</param> /// <param name="IL">The <see cref="CilWorker"/> instance that represents the method body.</param> /// <param name="oldInstructions">The IL instructions of the original method body.</param> protected override void RewriteMethodBody(MethodDefinition targetMethod, CilWorker IL, IEnumerable<Instruction> oldInstructions) { var endOfOriginalInstructionBlock = IL.Create(OpCodes.Nop); var addOriginalInstructions = new AddOriginalInstructions(oldInstructions, endOfOriginalInstructionBlock); var endLabel = IL.Create(OpCodes.Nop); var tryStart = IL.Emit(OpCodes.Nop); var tryEnd = IL.Emit(OpCodes.Nop); var catchStart = IL.Emit(OpCodes.Nop); var catchEnd = IL.Emit(OpCodes.Nop); var module = IL.GetModule(); var handler = new ExceptionHandler(ExceptionHandlerType.Catch); var body = targetMethod.Body; body.ExceptionHandlers.Add(handler); handler.CatchType = module.ImportType<Exception>(); handler.TryStart = tryStart; handler.TryEnd = tryEnd; handler.HandlerStart = catchStart; handler.HandlerEnd = catchEnd; var emitter = new InvocationInfoEmitter(true); var returnType = targetMethod.ReturnType.ReturnType; // try { IL.Append(tryStart); addOriginalInstructions.Emit(IL); IL.Append(endOfOriginalInstructionBlock); if (returnType != _voidType && _returnValue != null) { IL.Emit(OpCodes.Stloc, _returnValue); } IL.Emit(OpCodes.Leave, endLabel); // } IL.Append(tryEnd); // catch (Exception ex) { IL.Append(catchStart); IL.Emit(OpCodes.Stloc, _exception); SaveExceptionInfo(targetMethod, emitter); IL.Emit(OpCodes.Ldloc, _exceptionInfo); var getHandlerMethodInfo = typeof (ExceptionHandlerRegistry).GetMethod("GetHandler"); var getHandlerMethod = module.Import(getHandlerMethodInfo); IL.Emit(OpCodes.Call, getHandlerMethod); IL.Emit(OpCodes.Stloc, _exceptionHandler); // if (exceptionHandler == null) // throw; var doRethrow = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, _exceptionHandler); IL.Emit(OpCodes.Brfalse, doRethrow); // if (handler.CanCatch(exceptionInfo)) { var leaveBlock = IL.Create(OpCodes.Nop); var canCatch = module.ImportMethod<IExceptionHandler>("CanCatch"); IL.Emit(OpCodes.Ldloc, _exceptionHandler); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Callvirt, canCatch); IL.Emit(OpCodes.Brfalse, doRethrow); var catchMethod = module.ImportMethod<IExceptionHandler>("Catch"); IL.Emit(OpCodes.Ldloc, _exceptionHandler); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Callvirt, catchMethod); // } var getShouldSkipRethrow = module.ImportMethod<IExceptionHandlerInfo>("get_ShouldSkipRethrow"); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Callvirt, getShouldSkipRethrow); IL.Emit(OpCodes.Brfalse, doRethrow); IL.Emit(OpCodes.Br, leaveBlock); IL.Append(doRethrow); IL.Emit(OpCodes.Rethrow); IL.Append(leaveBlock); IL.Emit(OpCodes.Leave, endLabel); IL.Append(catchEnd); // } IL.Append(endLabel); if (returnType != _voidType && _returnValue != null) { var returnOriginalValue = IL.Create(OpCodes.Nop); var getReturnValue = module.ImportMethod<IExceptionHandlerInfo>("get_ReturnValue"); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Brfalse, returnOriginalValue); IL.Emit(OpCodes.Ldloc, _exceptionInfo); IL.Emit(OpCodes.Callvirt, getReturnValue); IL.Emit(OpCodes.Stloc, _returnValue); IL.Append(returnOriginalValue); IL.Emit(OpCodes.Ldloc, _returnValue); } IL.Emit(OpCodes.Ret); }
private void InstrumentMethodAtReturn(CilWorker worker, Instruction exitInstruction, MethodReference exitingMethod, MethodDefinition methodToInstrument) { exitInstruction.OpCode = OpCodes.Ldstr; exitInstruction.Operand = MethodName(methodToInstrument); Instruction callToProfiler = worker.Create(OpCodes.Call, exitingMethod); worker.InsertAfter(exitInstruction, callToProfiler); worker.InsertAfter(callToProfiler, worker.Create(OpCodes.Ret)); }