public override MethodDefinition BeforeMethod(MethodDefinition method) { if (!method.HasBody) { return(method); } TryDeclareExceptionsForLoggers(method); var calls = callCollector.Collect(method, logDefinitionFactory.IsLogger); if (calls.Length == 0) { return(method); } var collapsed = calls.SplitToSequences(new SameMethodComparer()).ToList(); if (collapsed.Count == 0) { return(method); } log.Info($"Rewriting {method.FullName}"); using (var builder = new BodyBuilder(method)) { foreach (var callGroup in collapsed) { var theCall = callGroup.First(); if (!logDefinitionFactory.TryGet(theCall.Call.TargetMethod().DeclaringType.Resolve(), out var info)) { throw new InvalidOperationException("Log info should have been cached"); } var start = theCall.StartsAt; // where the arguments start var label = builder.DefineLabel(); // jump target var end = callGroup.Last().Call; // the last call/callvirt in the sequence var field = info.DeclareLoggerIn(method.DeclaringType); // the static field containing the logger instance var guard = info.TryFindGuard(end); // the IsXXXEnabled method (optional) if (guard == null) { log.Warn($"There is no IsXXXEnabled defined for {end.TargetMethod()}, no check will be emitted"); } else { builder.InsertBefore(start, Instruction.Create(OpCodes.Ldsfld, field)); builder.InsertBefore(start, Instruction.Create(OpCodes.Callvirt, guard)); builder.InsertBefore(start, Instruction.Create(OpCodes.Brfalse, label)); } var logMethod = info.MapToILog(end); foreach (var call in callGroup) { builder.InsertBefore(call.StartsAt, Instruction.Create(OpCodes.Ldsfld, field)); call.Call.OpCode = OpCodes.Callvirt; call.Call.Operand = logMethod; } if (guard != null) { builder.InsertAfter(end, label); } } } return(method); }