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]; if (instruction.Operand is MethodReference methodReference) { if (string.Equals(methodReference.Name, "GetCurrentClassLogger")) { // We have a possible match var getLoggerMethod = GetGetLoggerMethod(methodReference.DeclaringType); if (getLoggerMethod is 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.WriteWarningPoint(message, point); } else { FodyEnvironment.WriteWarning(message); } continue; } FodyEnvironment.WriteDebug($"Weaving auto log to specific log for '{type.FullName}'"); 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) { if (!(instruction.Operand is MethodReference methodBeingCalled)) { 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 is null) { return(false); } if (!firstParameter.ParameterType.FullName.Contains("System.Linq.Expressions.")) { return(false); } var finalKey = methodBeingCalled.GetFullName(); if (!ExpressionChecksToAttributeMappings.ContainsKey(finalKey)) { FodyEnvironment.WriteWarningPoint($"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.GetSequencePoint(instruction)); return(false); } return(true); }
private void ProcessMethod(MethodDefinition method) { if (method.Body is null) { return; } if (method.IsDecoratedWithAttribute("NoWeavingAttribute")) { FodyEnvironment.WriteDebug($"\t\tSkipping '{method.Name}' because 'Catel.Fody.NoWeavingAttribute'"); 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.WriteDebug($"\tExecuting '{GetType().Name}' for '{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 is 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.WriteErrorPoint($"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 beginning of the ctor) if (instructions != null || ContainsArgumentChecks(method)) { if (instructions is 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)) { if (_configuration.IsRunningAgainstCatel) { FodyEnvironment.WriteError($"Weaving argument checks is disabled for Catel itself, ensure writing performant code by calling the non-expression version in '{method.GetFullName()}'"); continue; } var fullKey = ((MethodReference)instruction.Operand).GetFullName(); var parameterOrField = GetParameterOrFieldForExpressionArgumentCheck(method, instructions, instruction); if (parameterOrField is null) { FodyEnvironment.WriteWarning($"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 is null) { FodyEnvironment.WriteWarningPoint($"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.GetSequencePoint(instruction)); continue; } var removedInfo = RemoveArgumentWeavingCall(method, instructions, instruction); if (!displayClasses.Contains(removedInfo.DisplayClassTypeDefinition)) { displayClasses.Add(removedInfo.DisplayClassTypeDefinition); } var weaver = ArgumentMethodCallWeaverBase.WellKnownWeavers[customAttribute.AttributeType.FullName]; if (!weaver.Execute(_typeDefinition, method, parameterOrField, customAttribute, removedInfo.Index)) { // 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); } } // Step 4) Remove double nop commands, start at 1 // Note: disabled because there might be jump codes to different Nop instructions //for (int i = 1; i < instructions.Count; i++) //{ // if (instructions[i].IsOpCode(OpCodes.Nop) && instructions[i - 1].IsOpCode(OpCodes.Nop)) // { // instructions.RemoveAt(i--); // } //} } if (instructions != null) { method.Body.OptimizeMacros(); method.UpdateDebugInfo(); } }