/// <summary> /// Emits an implementation of a given method. /// </summary> /// <param name="invocationContext">The InvocationContext for this call.</param> /// <param name="eventId">The next eventID to use.</param> /// <param name="autoKeyword">The auto-keyword to use if enabled.</param> /// <returns>The method that is implemented.</returns> private MethodBuilder EmitMethodImpl(InvocationContext invocationContext, ref int eventId, EventKeywords autoKeyword) { // get the method we are implementing and the parameter mapping var interfaceMethod = invocationContext.MethodInfo; var parameterMapping = _traceParameterProvider.ProvideParameterMapping(invocationContext.MethodInfo).Where(p => p.HasSource).ToList(); // if we are implementing an interface, then add an string context parameter if (SupportsContext(invocationContext)) { parameterMapping.Add(new ParameterMapping(Context)); } // calculate the method name // if there is more than one method with the given name, then append an ID to it var methodName = interfaceMethod.Name; var matchingMethods = interfaceMethod.DeclaringType.GetMethods().AsEnumerable().Where(im => String.Compare(im.Name, methodName, StringComparison.OrdinalIgnoreCase) == 0).ToArray(); if (matchingMethods.Length > 1) { methodName += "_" + Array.IndexOf(matchingMethods, interfaceMethod).ToString(CultureInfo.InvariantCulture); } // determine if this is a non-event or an event // if an event, but there is no event attribute, just add one to the last event id EventAttribute eventAttribute = null; if (interfaceMethod.GetCustomAttribute <NonEventAttribute>() == null) { eventAttribute = _eventAttributeProvider.GetEventAttribute(invocationContext, eventId); eventId = Math.Max(eventId, eventAttribute.EventId + 1); } // if auto-keywords are enabled, use them if (eventAttribute != null && eventAttribute.Keywords == EventKeywords.None) { eventAttribute.Keywords = autoKeyword; } // create the internal method that calls WriteEvent // this cannot be virtual or static, or the manifest builder will skip it // it also cannot return a value MethodBuilder m = _typeBuilder.DefineMethod(methodName, MethodAttributes.Public, typeof(void), parameterMapping.Select(p => p.CleanTargetType).ToArray()); // copy the Event or NonEvent attribute from the interface if (eventAttribute != null) { m.SetCustomAttribute(EventAttributeHelper.ConvertEventAttributeToAttributeBuilder(eventAttribute)); } else { m.SetCustomAttribute(EventAttributeHelper.CreateNonEventAttribute()); } // add the parameter names for (int i = 0; i < parameterMapping.Count; i++) { var parameter = parameterMapping[i]; m.DefineParameter(i + 1, ParameterAttributes.In, parameter.Name); } if (interfaceMethod.IsAbstract || !interfaceMethod.DeclaringType.IsSubclassOf(typeof(EventSource))) { // for interface methods, implement a call to write event ProxyHelper.EmitDefaultValue(m.GetILGenerator(), m.ReturnType); if (EmitIsEnabled(m, eventAttribute)) { EmitCallWriteEvent(invocationContext, m, eventAttribute, parameterMapping); } // since EventSource only accepts non-virtual methods, and we need a virtual method to implement the abstract method // we need to implement a wrapper method on the interface that calls into the base method // and handles the bundling/unbundling of parameters MethodBuilder im = _typeBuilder.DefineMethod("_" + methodName, MethodAttributes.Public | MethodAttributes.Virtual); ProxyHelper.CopyMethodSignature(interfaceMethod, im); ProxyHelper.EmitDefaultValue(im.GetILGenerator(), im.ReturnType); if (EmitIsEnabled(im, eventAttribute)) { EmitDirectProxy(invocationContext, im, m, parameterMapping); } // if this is an interface, then tell the system to map our method to the interface implementation if (interfaceMethod.IsAbstract) { _typeBuilder.DefineMethodOverride(im, interfaceMethod); } } else { // we are implementing a non-abstract method in event source, then // all we can do is call the base implementation EmitDirectProxy(invocationContext, m, interfaceMethod, parameterMapping); } return(m); }
/// <summary> /// Emits the implementation of a given interface method. /// </summary> /// <param name="executeMethod">The execute method to implement.</param> private void EmitMethodImpl(MethodInfo executeMethod, List <Tuple <FieldInfo, object> > valuesToSet) { /* * public TReturn Method (params) * { * var scope = new EventActivityScope(true); * try * { * _log.Method(params); * object value = _execute.Method(params); * _log.Method_Completed(value); * } * catch (Exception e) * { * _log.Method_Faulted(e); * throw; * } * finally * { * scope.Dispose(); * } * } */ var invocationContext = new InvocationContext(executeMethod, InvocationContextTypes.MethodCall); // start building the interface MethodBuilder m = _typeBuilder.DefineMethod(executeMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual); ProxyHelper.CopyMethodSignature(executeMethod, m); var parameterTypes = executeMethod.GetParameters().Select(p => p.ParameterType).ToArray(); ILGenerator mIL = m.GetILGenerator(); // set up a place to hold the return value LocalBuilder returnValue = null; if (m.ReturnType != typeof(void)) { returnValue = mIL.DeclareLocal(m.ReturnType); } // set up the activity scope LocalBuilder scope = null; if (_callWithActivityScope) { scope = mIL.DeclareLocal(typeof(EventActivityScope)); mIL.Emit(OpCodes.Ldc_I4_1); mIL.Emit(OpCodes.Newobj, typeof(EventActivityScope).GetConstructor(new Type[] { typeof(bool) })); mIL.Emit(OpCodes.Stloc, scope); } // start the try block mIL.BeginExceptionBlock(); // call the method on the log that matches the execute method var targetParameterTypes = parameterTypes.Select(p => p.IsGenericParameter ? TypeImplementer.GetTypeSupportedByEventSource(p) : p).ToArray(); var logMethod = DiscoverMethod(_logField.FieldType, executeMethod.Name, String.Empty, targetParameterTypes); if (logMethod != null) { // call the log method and throw away the result if there is one EmitBaseMethodCall(m, invocationContext, _logField, executeMethod, logMethod, valuesToSet); if (logMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Pop); } } // call execute EmitBaseMethodCall(m, invocationContext, _executeField, executeMethod, executeMethod, valuesToSet); if (executeMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Stloc, returnValue); } // if there is a completed method, then call that var completedParameterTypes = (executeMethod.ReturnType == typeof(void)) ? Type.EmptyTypes : new Type[] { executeMethod.ReturnType }; var completedMethod = DiscoverMethod(_logField.FieldType, executeMethod.Name, TypeImplementer.CompletedSuffix, completedParameterTypes); if (completedMethod != null) { // load this._log mIL.Emit(OpCodes.Ldarg_0); mIL.Emit(OpCodes.Ldfld, _logField); // load the value from the local variable if (executeMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Ldloc, returnValue); } mIL.Emit(OpCodes.Call, completedMethod); if (completedMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Pop); } } // handle exceptions by logging them and rethrowing mIL.BeginCatchBlock(typeof(Exception)); var faultedMethod = DiscoverMethod(_logField.FieldType, executeMethod.Name, TypeImplementer.FaultedSuffix, new Type[] { typeof(Exception) }); if (faultedMethod != null) { // save the exception var exception = mIL.DeclareLocal(typeof(Exception)); mIL.Emit(OpCodes.Stloc, exception); // load this._log mIL.Emit(OpCodes.Ldarg_0); mIL.Emit(OpCodes.Ldfld, _logField); // load the exception mIL.Emit(OpCodes.Ldloc, exception); // call the fault handler mIL.Emit(OpCodes.Call, faultedMethod); if (faultedMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Pop); } } mIL.Emit(OpCodes.Rethrow); // clean up the activity scope if (_callWithActivityScope) { mIL.BeginFinallyBlock(); mIL.Emit(OpCodes.Ldloc, scope); mIL.Emit(OpCodes.Callvirt, typeof(EventActivityScope).GetMethod("Dispose")); } mIL.EndExceptionBlock(); // return the result if (executeMethod.ReturnType != typeof(void)) { mIL.Emit(OpCodes.Ldloc, returnValue); } mIL.Emit(OpCodes.Ret); }