public void Execute(CatelType catelType)
        {
            if (catelType.TypeDefinition.IsAbstract)
            {
                return;
            }

            if (catelType.TypeDefinition.IsEnum)
            {
                return;
            }

            if (!catelType.TypeDefinition.ImplementsCatelModel())
            {
                return;
            }

            if (catelType.TypeDefinition.ImplementsViewModelBase())
            {
                return;
            }

            FodyEnvironment.LogDebug("\t\t Adding xml schema for type " + catelType.TypeDefinition.FullName);

            if (AddXmlSchemaProviderAttribute(catelType))
            {
                AddGetXmlSchemaMethod(catelType);
            }
        }
        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)));
                    }
                }
            }
        }
Beispiel #3
0
 private void Process(List <CatelType> catelTypes)
 {
     foreach (var catelType in catelTypes)
     {
         foreach (var propertyData in catelType.Properties.ToList())
         {
             var warning = CheckForWarning(propertyData);
             if (warning != null)
             {
                 FodyEnvironment.LogDebug($"\t{propertyData.PropertyDefinition.GetName()} {warning} property will be ignored.");
                 catelType.Properties.Remove(propertyData);
             }
         }
     }
 }
Beispiel #4
0
        private bool IsPrivateReference(string assemblyName)
        {
            var privateReferences = FindPrivateReferences();

            var userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            var nuGetRoot       = Path.Combine(userProfilePath, ".nuget", "packages");

            // For now, ignore the target version, just check whether the package (version) contains the assembly
            foreach (var privateReference in privateReferences)
            {
                try
                {
                    // For some packages (such as Fody), there is no /lib folder, in that case we don't need
                    // to check anything
                    var path = Path.Combine(nuGetRoot, privateReference.PackageName, privateReference.Version, "lib");
                    if (!Directory.Exists(path))
                    {
                        continue;
                    }

                    FodyEnvironment.LogDebug($"Checking private reference '{privateReference}' using '{path}'");

                    var isDll = Directory.GetFiles(path, $"{assemblyName}.dll", SearchOption.AllDirectories).Any();
                    if (isDll)
                    {
                        return(true);
                    }

                    var isExe = Directory.GetFiles(path, $"{assemblyName}.exe", SearchOption.AllDirectories).Any();
                    if (isExe)
                    {
                        return(true);
                    }
                }
                catch (Exception ex)
                {
                    FodyEnvironment.LogError($"Failed to check private reference '{privateReference}':\n{ex}");
                }
            }

            return(false);
        }
        private void Process(List <CatelType> catelTypes)
        {
            foreach (var catelType in catelTypes)
            {
                if (catelType.SetValueInvoker == null)
                {
                    continue;
                }

                if (_configuration.WeaveCalculatedProperties)
                {
                    var onPropertyChangedWeaver = new OnPropertyChangedWeaver(catelType, _msCoreReferenceFinder);
                    onPropertyChangedWeaver.Execute();
                }

                FodyEnvironment.LogDebug("\t" + catelType.TypeDefinition.FullName);

                foreach (var propertyData in catelType.Properties)
                {
                    if (AlreadyContainsCallToMember(propertyData.PropertyDefinition, catelType.GetValueInvoker.Name) ||
                        AlreadyContainsCallToMember(propertyData.PropertyDefinition, catelType.SetValueInvoker.Name))
                    {
                        FodyEnvironment.LogDebug($"\t{propertyData.PropertyDefinition.GetName()} already has GetValue and/or SetValue functionality. Property will be ignored.");
                        continue;
                    }

                    var body = propertyData.PropertyDefinition.SetMethod.Body;

                    body.SimplifyMacros();

                    var propertyWeaver = new CatelPropertyWeaver(catelType, propertyData, _msCoreReferenceFinder);
                    propertyWeaver.Execute();

                    body.InitLocals = true;
                    body.OptimizeMacros();
                }
            }
        }
        private void RemoveObsoleteCodeForArgumentExpression(MethodDefinition method, Collection <Instruction> instructions, TypeDefinition displayClassType)
        {
            // Display class is used when there are still calls to load a field from the display class
            if (instructions.UsesType(displayClassType, OpCodes.Ldfld, OpCodes.Ldftn))
            {
                return;
            }

            FodyEnvironment.LogDebug($"Method '{method.GetFullName()}' no longer uses display class '{displayClassType.GetFullName()}', removing the display class from the method");

            // Remove display class from container
            if (method.DeclaringType.NestedTypes.Contains(displayClassType))
            {
                method.DeclaringType.NestedTypes.Remove(displayClassType);
            }

            // Remove display class - variables
            for (var i = 0; i < method.Body.Variables.Count; i++)
            {
                var variable = method.Body.Variables[i];
                if (string.Equals(variable.VariableType.Name, displayClassType.Name))
                {
                    method.Body.Variables.RemoveAt(i--);
                }
            }

            // Remove display class creation, can be either:
            //
            // Msbuild
            //   L_0000: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass1a::.ctor()
            //   L_0005: stloc.0
            //
            // Roslyn
            //   L_0000: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass1a::.ctor()
            //   L_0005: dup

            for (var i = 0; i < instructions.Count; i++)
            {
                var innerInstruction = instructions[i];
                if (innerInstruction.OpCode == OpCodes.Newobj)
                {
                    var remove = false;

                    var methodReference = innerInstruction.Operand as MethodReference;
                    if (methodReference != null)
                    {
                        if (string.Equals(methodReference.DeclaringType.Name, displayClassType.Name))
                        {
                            remove = true;
                        }
                    }

                    var methodDefinition = innerInstruction.Operand as MethodDefinition;
                    if (methodDefinition != null)
                    {
                        if (string.Equals(methodDefinition.DeclaringType.Name, displayClassType.Name))
                        {
                            remove = true;
                        }
                    }

                    if (remove)
                    {
                        // Delete 2 instructions, same location since remove will move everything 1 place up
                        instructions.RemoveAt(i);

                        // Special case in .net core
                        if (instructions[i].OpCode == OpCodes.Dup)
                        {
                            instructions.RemoveAt(i);
                        }

                        instructions.RemoveAt(i);
                    }
                }
            }

            //// Remove all assignments to the display class
            ////   ldarg.0
            ////   stfld class MyClass/<>c__DisplayClass0_0`1<!!T>::myArgument

            //for (var i = 0; i < instructions.Count; i++)
            //{
            //    var innerInstruction = instructions[i];
            //}

            // Remove display class allocation and assigments
            //   L_0014: ldloc.0 (can also be dup)
            //   L_0015: ldarg.3
            //   L_0016: stfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass28::myObject3

            for (var i = 0; i < instructions.Count; i++)
            {
                var innerInstruction = instructions[i];
                if (innerInstruction.UsesType(displayClassType, OpCodes.Stfld))
                {
                    // Remove the stfld + 2 previous operations
                    instructions.RemoveAt(i);
                    instructions.RemoveAt(i - 1);
                    instructions.RemoveAt(i - 2);

                    i -= 3;
                }
            }

            // Remove duplicate nop instructions at the start of a method
            if (instructions.Count > 0)
            {
                var startInstruction = instructions[0];
                if (startInstruction.IsOpCode(OpCodes.Nop))
                {
                    instructions.RemoveAt(0);
                }
            }
        }
Beispiel #7
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();
            }
        }