public void Execute() { var loggingFields = (from field in _type.Fields where field.IsStatic && string.Equals(field.FieldType.FullName, CatelLoggingClass) select field).ToList(); if (loggingFields.Count == 0) { return; } var staticConstructor = _type.GetStaticConstructor(); if (staticConstructor == null) { FodyEnvironment.LogWarning($"Cannot weave ILog fields without a static constructor, ignoring type '{_type.FullName}'"); return; } var body = staticConstructor.Body; body.SimplifyMacros(); try { UpdateCallsToGetCurrentClassLogger(body); } catch (Exception ex) { FodyEnvironment.LogWarning($"Failed to update static log definition in '{_type.FullName}', '{ex.Message}'"); } body.OptimizeMacros(); staticConstructor.UpdateDebugInfo(); }
private bool AddOrUpdateOnPropertyChangedMethod(PropertyDefinition property) { var getMethodReference = _catelType.TypeDefinition.Module.ImportReference(_catelType.AdvancedPropertyChangedEventArgsType.GetProperty("PropertyName").Resolve().GetMethod); var stringEqualsMethodReference = _catelType.TypeDefinition.Module.ImportReference(GetSystemObjectEqualsMethodReference(_catelType.TypeDefinition.Module)); var dependentProperties = _catelType.GetDependentPropertiesFrom(property).ToList(); if (dependentProperties.Count > 0 && !dependentProperties.All(definition => _catelType.NoWeavingProperties.Contains(definition))) { var onPropertyChangedMethod = EnsureOnPropertyChangedMethod(); if (onPropertyChangedMethod == null) { FodyEnvironment.LogWarning($"No call to base.OnPropertyChanged(e) or a custom implementation in '{property.DeclaringType.Name}', cannot weave this method to automatically raise on dependent property change notifications"); return(false); } var idx = onPropertyChangedMethod.Body.Instructions.ToList().FindLastIndex(instruction => instruction.OpCode == OpCodes.Ret); if (idx > -1) { var booleanTypeReference = _catelType.TypeDefinition.Module.ImportReference(_msCoreReferenceFinder.GetCoreTypeReference("Boolean")); if (onPropertyChangedMethod.Body.Variables.ToList().FirstOrDefault(definition => definition.VariableType != booleanTypeReference) == null) { onPropertyChangedMethod.Body.Variables.Add(new VariableDefinition(booleanTypeReference)); onPropertyChangedMethod.Body.InitLocals = true; } foreach (var propertyDefinition in dependentProperties) { if (_catelType.NoWeavingProperties.Contains(propertyDefinition)) { continue; } onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldarg_1)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Callvirt, getMethodReference)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldstr, property.Name)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Callvirt, stringEqualsMethodReference)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldc_I4_0)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ceq)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Stloc_0)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldloc_0)); var jmpIdx = idx++; onPropertyChangedMethod.Body.Instructions.Insert(jmpIdx, Instruction.Create(OpCodes.Nop)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldarg_0)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldstr, propertyDefinition.Name)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Call, _catelType.RaisePropertyChangedInvoker)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop)); onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop)); onPropertyChangedMethod.Body.Instructions[jmpIdx] = Instruction.Create(OpCodes.Brtrue_S, onPropertyChangedMethod.Body.Instructions[idx - 1]); } } } return(true); }
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))); } } } }
public void Execute() { foreach (var type in _allTypes) { try { var weaver = new LoggingWeaver(type); weaver.Execute(); } catch (Exception ex) { FodyEnvironment.LogWarning($"Failed to weave type '{type.FullName}', message is '{ex.Message}'"); return; } } }
private List <PrivateReference> FindPrivateReferences() { var csProj = _moduleWeaver.ProjectFilePath; if (string.IsNullOrWhiteSpace(csProj) || !File.Exists(csProj)) { return(new List <PrivateReference>()); } // Assembly name != package name, so we need to go through all *private* packages to // see if it's a private reference. For now we have a *simple* reference structure, // which means only modern sdk project style is supported, and only references directly // listed in the csproj will be supported var privateReferencesCache = CacheHelper.GetCache <Dictionary <string, List <PrivateReference> > >(csProj); if (!privateReferencesCache.TryGetValue(csProj, out var privateReferences)) { privateReferences = new List <PrivateReference>(); try { var element = XElement.Parse(File.ReadAllText(csProj)); var packageReferenceElements = element.XPathSelectElements("//PackageReference"); foreach (var packageReferenceElement in packageReferenceElements) { var includeAttribute = packageReferenceElement.Attribute("Include"); if (includeAttribute is null) { continue; } var packageName = includeAttribute.Value; var versionAttribute = packageReferenceElement.Attribute("Version"); if (versionAttribute is null) { FodyEnvironment.LogWarning($"Could not find version attribute for '{packageName}'"); continue; } var version = versionAttribute.Value; var privateAssetsAttribute = packageReferenceElement.Attribute("PrivateAssets"); if (privateAssetsAttribute != null) { if (string.Equals(privateAssetsAttribute.Value, "all", StringComparison.OrdinalIgnoreCase)) { privateReferences.Add(new PrivateReference(packageName, version)); continue; } } var privateAssetsElement = packageReferenceElement.Element("PrivateAssets"); if (privateAssetsElement != null) { if (string.Equals(privateAssetsElement.Value, "all", StringComparison.OrdinalIgnoreCase)) { privateReferences.Add(new PrivateReference(packageName, version)); continue; } } } } catch (Exception ex) { FodyEnvironment.LogError($"Failed to search for private packages in project file '{csProj}':\n{ex}"); } privateReferencesCache[csProj] = privateReferences; } return(privateReferences); }
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(); } }