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;
         }
     }
 }
Exemple #5
0
        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);
        }
Exemple #6
0
        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();
            }
        }