private void UpdateCallsToGetCurrentClassLogger(MethodBody ctorBody) { // Convert this: // // call class [Catel.Core]Catel.Logging.ILog [Catel.Core]Catel.Logging.LogManager::GetCurrentClassLogger() // stsfld class [Catel.Core]Catel.Logging.ILog Catel.Fody.TestAssembly.LoggingClass::AutoLog // // into this: // // ldtoken Catel.Fody.TestAssembly.LoggingClass // call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) // call class [Catel.Core]Catel.Logging.ILog [Catel.Core]Catel.Logging.LogManager::GetLogger(class [mscorlib]System.Type) // stsfld class [Catel.Core]Catel.Logging.ILog Catel.Fody.TestAssembly.LoggingClass::ManualLog var type = ctorBody.Method.DeclaringType; var instructions = ctorBody.Instructions; for (var i = 0; i < instructions.Count; i++) { var instruction = instructions[i]; var methodReference = instruction.Operand as MethodReference; if (methodReference != null) { FodyEnvironment.LogDebug($"Weaving auto log to specific log for '{type.FullName}'"); if (string.Equals(methodReference.Name, "GetCurrentClassLogger")) { // We have a possible match var getLoggerMethod = GetGetLoggerMethod(methodReference.DeclaringType); if (getLoggerMethod == null) { var point = methodReference.Resolve().GetSequencePoint(instruction); var message = $"Cannot change method call for log '{type.FullName}', the GetLogger(type) method does not exist on the calling type (try to use LogManager.GetCurrentClassLogger())"; if (point != null) { FodyEnvironment.LogWarningPoint(message, point); } else { FodyEnvironment.LogWarning(message); } continue; } var getTypeFromHandle = type.Module.GetMethodAndImport("GetTypeFromHandle"); instructions.RemoveAt(i); instructions.Insert(i, Instruction.Create(OpCodes.Ldtoken, type), Instruction.Create(OpCodes.Call, getTypeFromHandle), Instruction.Create(OpCodes.Call, type.Module.ImportReference(getLoggerMethod))); } } } }
private bool IsSupportedExpressionArgumentCheck(MethodDefinition method, Instruction instruction) { var methodBeingCalled = instruction.Operand as MethodReference; if (methodBeingCalled == null) { return(false); } if (!methodBeingCalled.DeclaringType.FullName.Contains("Catel.Argument")) { return(false); } var parameters = methodBeingCalled.Parameters; if (parameters.Count == 0) { return(false); } var firstParameter = parameters[0]; if (firstParameter == null) { return(false); } if (!firstParameter.ParameterType.FullName.Contains("System.Linq.Expressions.")) { return(false); } var finalKey = methodBeingCalled.GetFullName(); if (!ExpressionChecksToAttributeMappings.ContainsKey(finalKey)) { FodyEnvironment.LogWarningPoint($"Expression argument method transformation in '{method.GetFullName()}' to '{methodBeingCalled.GetFullName()}' is not (yet) supported. To ensure the best performance, either rewrite this into a non-expression argument check or create a PR for Catel.Fody to enable support :-)", method.Body.Instructions.GetSequencePoint(instruction)); return(false); } return(true); }
private void ProcessMethod(MethodDefinition method) { if (method.Body == null) { return; } // Note: very important to only simplify/optimize methods that we actually change, otherwise some Mono.Cecil bugs // will appear on the surface Collection <Instruction> instructions = null; var methodFullName = method.GetFullName(); FodyEnvironment.LogDebug($"Processing method '{methodFullName}'"); // Step 1) Convert attributes // TODO: how to handle async/await here? for (var i = method.Parameters.Count - 1; i >= 0; i--) { var parameter = method.Parameters[i]; for (var j = parameter.CustomAttributes.Count - 1; j >= 0; j--) { var customAttribute = parameter.CustomAttributes[j]; var attributeFullName = customAttribute.AttributeType.FullName; if (ArgumentMethodCallWeaverBase.WellKnownWeavers.ContainsKey(attributeFullName)) { if (instructions == null) { method.Body.SimplifyMacros(); instructions = method.Body.Instructions; } ArgumentMethodCallWeaverBase.WellKnownWeavers[attributeFullName].Execute(_typeDefinition, method, parameter, customAttribute, 0); parameter.RemoveAttribute(attributeFullName); } else if (attributeFullName.StartsWith("Catel.Fody")) { FodyEnvironment.LogErrorPoint($"Weaving of parameter '{method.GetFullName()}' of methods '{parameter.Name}' with attribute '{attributeFullName}' is not (yet) supported, please use a different method", method.GetFirstSequencePoint()); } } } // Step 2) Convert expressions to normal calls var displayClasses = new List <TypeDefinition>(); // Go backwards to keep the order of the arguments correct (because argument checks are injected at the beginnen of the ctor) if (instructions != null || ContainsArgumentChecks(method)) { if (instructions == null) { method.Body.SimplifyMacros(); instructions = method.Body.Instructions; } for (var i = instructions.Count - 1; i >= 0; i--) { var instruction = instructions[i]; if (IsSupportedExpressionArgumentCheck(method, instruction)) { var fullKey = ((MethodReference)instruction.Operand).GetFullName(); var parameterOrField = GetParameterOrFieldForExpressionArgumentCheck(method, instructions, instruction); if (parameterOrField == null) { FodyEnvironment.LogWarning($"Cannot weave at least one argument of method '{method.GetFullName()}'"); continue; } if (!ExpressionChecksToAttributeMappings.ContainsKey(fullKey)) { return; } var customAttribute = ExpressionChecksToAttributeMappings[fullKey](method, instructions, instruction); if (customAttribute == null) { FodyEnvironment.LogWarningPoint($"Expression argument method transformation in '{method.GetFullName()}' to '{fullKey}' is not (yet) supported. To ensure the best performance, either rewrite this into a non-expression argument check or create a PR for Catel.Fody to enable support :-)", method.Body.Instructions.GetSequencePoint(instruction)); continue; } var removedInfo = RemoveArgumentWeavingCall(method, instructions, instruction); if (!displayClasses.Contains(removedInfo.Item1)) { displayClasses.Add(removedInfo.Item1); } var weaver = ArgumentMethodCallWeaverBase.WellKnownWeavers[customAttribute.AttributeType.FullName]; if (!weaver.Execute(_typeDefinition, method, parameterOrField, customAttribute, removedInfo.Item2)) { // We failed, the build should fail now return; } // Reset counter, start from the beginning i = instructions.Count - 1; } } // Step 3) Clean up unnecessary code if (displayClasses.Count > 0) { foreach (var displayClass in displayClasses) { RemoveObsoleteCodeForArgumentExpression(method, instructions, displayClass); } } } if (instructions != null) { method.Body.OptimizeMacros(); } }