Example #1
0
        public CatelType(TypeDefinition typeDefinition)
        {
            Mappings   = new List <MemberMapping>();
            Properties = new List <CatelTypeProperty>();

            Ignore         = true;
            TypeDefinition = typeDefinition;
            Name           = typeDefinition.FullName;

            DetermineCatelType();
            if (Type == CatelTypeType.Unknown)
            {
                FodyEnvironment.LogWarning($"Cannot determine the Catel type used for '{Name}', type will be ignored for weaving");
                return;
            }

            try
            {
                DetermineTypes();

                if (!DetermineMethods())
                {
                    return;
                }

                Properties = DetermineProperties();
                DetermineMappings();

                Ignore = false;
            }
            catch (Exception)
            {
                FodyEnvironment.LogWarning($"Failed to get additional information about type '{Name}', type will be ignored for weaving");
            }
        }
Example #2
0
        private void EnsureStaticConstructor(TypeDefinition type)
        {
            var staticConstructor = type.Constructor(true);

            if (staticConstructor is null)
            {
                FodyEnvironment.WriteDebug($"\t\t\t{type.Name} - adding static constructor");

                var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void");

                staticConstructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static | MethodAttributes.RTSpecialName,
                                                         type.Module.ImportReference(voidType));

                var body = staticConstructor.Body;
                body.SimplifyMacros();

                body.Instructions.Add(Instruction.Create(OpCodes.Ret));

                body.OptimizeMacros();

                type.Methods.Add(staticConstructor);

                FodyEnvironment.WriteDebug($"\t\t\t{type.Name} - added static constructor");
            }
        }
        public void Execute()
        {
            var xmlDefinition = _assemblyResolver.Resolve("System.Xml");

            if (xmlDefinition != null)
            {
                var xmlTypes = xmlDefinition.MainModule.Types;

                XmlQualifiedName = (from t in xmlTypes
                                    where string.Equals(t.FullName, "System.Xml.XmlQualifiedName")
                                    select t).FirstOrDefault();

                XmlSchemaSet = (from t in xmlTypes
                                where string.Equals(t.FullName, "System.Xml.Schema.XmlSchemaSet")
                                select t).FirstOrDefault();
            }
            else
            {
                FodyEnvironment.WriteInfo("System.Xml not referenced, disabling xml-related features");
            }

            GeneratedCodeAttribute       = GetCoreTypeReference(GeneratedCodeAttributeTypeName);
            CompilerGeneratedAttribute   = GetCoreTypeReference(CompilerGeneratedAttributeTypeName);
            DebuggerNonUserCodeAttribute = GetCoreTypeReference(DebuggerNonUserCodeAttributeTypeName);
        }
        private void AddCatelTypeIfRequired(TypeDefinition typeDefinition)
        {
            if (typeDefinition?.BaseType is null)
            {
                return;
            }

            if (typeDefinition.IsDecoratedWithAttribute("Catel.Fody.NoWeavingAttribute"))
            {
                FodyEnvironment.WriteDebug($"\t{typeDefinition.FullName} is decorated with the NoWeaving attribute, type will be ignored.");

                typeDefinition.RemoveAttribute("Catel.Fody.NoWeavingAttribute");
                return;
            }

            if (!typeDefinition.ImplementsCatelModel())
            {
                return;
            }

            var typeNode = new CatelType(typeDefinition, _msCoreReferenceFinder);

            if (typeNode.Ignore || CatelTypes.Contains(typeNode))
            {
                return;
            }

            CatelTypes.Add(typeNode);
        }
Example #5
0
        private void DetermineMethods()
        {
            string methodName = $"On{PropertyDefinition.Name}Changed";

            var declaringType = PropertyDefinition.DeclaringType;

            var callbackReferences = (from method in declaringType.Methods
                                      where method.Name == methodName
                                      select method).ToList();

            foreach (var callbackReference in callbackReferences)
            {
                if (callbackReference != null)
                {
                    if (callbackReference.HasParameters)
                    {
                        FodyEnvironment.LogWarning($"Method '{declaringType.FullName}.{callbackReference.Name}' matches automatic change method name but has parameters and will not be used as automatic change callback. Rename the method to remove this warning or remove parameters to use as automatic callback method.");
                        continue;
                    }

                    MethodReference finalCallbackReference = callbackReference;
                    if (declaringType.HasGenericParameters)
                    {
                        finalCallbackReference = finalCallbackReference.MakeGeneric(declaringType);
                    }

                    ChangeCallbackReference = finalCallbackReference;
                    break;
                }
            }
        }
Example #6
0
        private void AddChangeNotificationHandlerField(PropertyDefinition property, CatelTypeProperty propertyData)
        {
            if (propertyData.ChangeCallbackReference == null)
            {
                return;
            }

            FodyEnvironment.LogDebug($"\t\t\t{property.Name} - adding On{property.Name}Changed invocation");

            var declaringType = property.DeclaringType;
            var fieldName     = GetChangeNotificationHandlerFieldName(property);

            var handlerType = GetEventHandlerAdvancedPropertyChangedEventArgs(property);
            var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs");

            //.field private static class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> CS$<>9__CachedAnonymousMethodDelegate1

            var field = new FieldDefinition(fieldName, FieldAttributes.Private | FieldAttributes.Static, declaringType.Module.ImportReference(handlerType));

            declaringType.Fields.Add(field);

            field.MarkAsCompilerGenerated(_msCoreReferenceFinder);

            //.method private hidebysig static void <.cctor>b__0(object s, class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs e) cil managed
            //{
            //    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
            //    .maxstack 8
            //    L_0000: ldarg.0
            //    L_0001: castclass Catel.Fody.TestAssembly.ModelBaseTest
            //    L_0006: callvirt instance void Catel.Fody.TestAssembly.ModelBaseTest::OnLastNameChanged()
            //    L_000b: nop
            //    L_000c: ret
            //}

            var voidType   = _msCoreReferenceFinder.GetCoreTypeReference("Void");
            var objectType = _msCoreReferenceFinder.GetCoreTypeReference("Object");

            var initializationMethodName = GetChangeNotificationHandlerConstructorName(property);
            var initializationMethod     = new MethodDefinition(initializationMethodName,
                                                                MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, declaringType.Module.ImportReference(voidType));

            initializationMethod.Parameters.Add(new ParameterDefinition("s", ParameterAttributes.None, declaringType.Module.ImportReference(objectType)));
            initializationMethod.Parameters.Add(new ParameterDefinition("e", ParameterAttributes.None, declaringType.Module.ImportReference(advancedPropertyChangedEventArgsType)));

            var body = initializationMethod.Body;

            body.Instructions.Insert(0,
                                     Instruction.Create(OpCodes.Ldarg_0),
                                     Instruction.Create(OpCodes.Castclass, declaringType.MakeGenericIfRequired()),
                                     Instruction.Create(OpCodes.Callvirt, propertyData.ChangeCallbackReference),
                                     Instruction.Create(OpCodes.Nop),
                                     Instruction.Create(OpCodes.Ret));

            declaringType.Methods.Add(initializationMethod);

            initializationMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder);
        }
Example #7
0
        public void Execute(bool force = false)
        {
            var property = _propertyData.PropertyDefinition;

            if (property == null)
            {
                FodyEnvironment.LogWarning("Skipping an unknown property because it has no property definition");
                return;
            }

            if (!force && !HasBackingField(property))
            {
                FodyEnvironment.LogDebug($"\t\tSkipping '{property.Name}' because it has no backing field");
                return;
            }

            if (ImplementsICommand(property))
            {
                FodyEnvironment.LogDebug($"\t\tSkipping '{property.Name}' because it implements ICommand");
                return;
            }

            FodyEnvironment.LogDebug("\t\t" + property.Name);

            try
            {
                EnsureStaticConstructor(property.DeclaringType);

                AddChangeNotificationHandlerField(property, _propertyData);

                var fieldDefinition = AddPropertyFieldDefinition(property);
                if (!AddPropertyRegistration(property, _propertyData))
                {
                    return;
                }

                var fieldReference = GetFieldReference(property.DeclaringType, fieldDefinition.Name, true);

                AddGetValueCall(property, fieldReference);
                AddSetValueCall(property, fieldReference, _propertyData.IsReadOnly);

                RemoveBackingField(property);
            }
            catch (Exception ex)
            {
                FodyEnvironment.LogError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}");

#if DEBUG
                Debugger.Launch();
#endif
            }
        }
Example #8
0
        private GenericInstanceType GetEventHandlerAdvancedPropertyChangedEventArgs(PropertyDefinition property)
        {
            var genericHandlerType = _msCoreReferenceFinder.GetCoreTypeReference("System.EventHandler`1");

            if (genericHandlerType == null)
            {
                FodyEnvironment.LogError("Expected to find EventHandler<T>, but type was not  found");
                return(null);
            }

            var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs");

            var handlerType = new GenericInstanceType(genericHandlerType);

            handlerType.GenericArguments.Add(advancedPropertyChangedEventArgsType);

            return(handlerType);
        }
Example #9
0
        public CatelType(TypeDefinition typeDefinition, MsCoreReferenceFinder msCoreReferenceFinder)
        {
            Mappings   = new List <MemberMapping>();
            Properties = new List <CatelTypeProperty>();

            Ignore                 = true;
            TypeDefinition         = typeDefinition;
            Name                   = typeDefinition.FullName;
            _msCoreReferenceFinder = msCoreReferenceFinder;

            DetermineCatelType();
            if (Type == CatelTypeType.Unknown)
            {
                FodyEnvironment.WriteWarning($"Cannot determine the Catel type used for '{Name}', type will be ignored for weaving");
                return;
            }

            Version = TypeDefinition.GetCatelVersion();
            if (Version == CatelVersion.Unknown)
            {
                FodyEnvironment.WriteWarning($"Cannot determine the Catel version used for '{Name}', type will be ignored for weaving");
                return;
            }

            try
            {
                DetermineTypes();

                if (!DetermineMethods())
                {
                    FodyEnvironment.WriteWarning($"Cannot determine the Catel methods used for '{Name}', type will be ignored for weaving");
                    return;
                }

                DetermineProperties();
                DetermineMappings();

                Ignore = false;
            }
            catch (Exception ex)
            {
                FodyEnvironment.WriteWarning($"Failed to get additional information about type '{Name}', type will be ignored for weaving: '{ex.Message}'");
            }
        }
Example #10
0
        private int AddGetValueCall(PropertyDefinition property, FieldReference fieldReference)
        {
            FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding GetValue call");

            var genericGetValue = new GenericInstanceMethod(_catelType.GetValueInvoker);

            foreach (var genericParameter in _catelType.GetValueInvoker.GenericParameters)
            {
                genericGetValue.GenericParameters.Add(genericParameter);
            }

            genericGetValue.GenericArguments.Add(property.PropertyType.Import());

            if (property.GetMethod is null)
            {
                var getMethod = new MethodDefinition($"get_{property.Name}", MethodAttributes.Public, property.PropertyType.Import());

                property.DeclaringType.Methods.Add(getMethod);

                getMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder);

                property.GetMethod = getMethod;
            }

            var body = property.GetMethod.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            instructions.Clear();

            var finalIndex = instructions.Insert(0,
                                                 Instruction.Create(OpCodes.Nop),
                                                 Instruction.Create(OpCodes.Ldarg_0),
                                                 Instruction.Create(OpCodes.Ldsfld, fieldReference),
                                                 Instruction.Create(OpCodes.Call, genericGetValue),
                                                 Instruction.Create(OpCodes.Ret));

            body.OptimizeMacros();

            return(finalIndex);
        }
Example #11
0
        private void AddChangeNotificationHandlerField(PropertyDefinition property, CatelTypeProperty propertyData)
        {
            if (propertyData.ChangeCallbackReference is null)
            {
                return;
            }

            FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding On{property.Name}Changed invocation");

            switch (_catelType.Version)
            {
            case CatelVersion.v5:
                AddChangeNotificationHandlerField_Catel5(property, propertyData);
                break;

            case CatelVersion.v6:
                AddChangeNotificationHandlerField_Catel6(property, propertyData);
                break;
            }
        }
Example #12
0
        public void Execute(bool force = false)
        {
            var property = _propertyData.PropertyDefinition;

            if (property is null)
            {
                FodyEnvironment.WriteWarning("Skipping an unknown property because it has no property definition");
                return;
            }

            if (!force && !HasBackingField(property))
            {
                FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no backing field");
                return;
            }

            if (!IsCleanSetter(property))
            {
                FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no clean setter (custom implementation?)");
                return;
            }

            FodyEnvironment.WriteDebug("\t\t" + property.Name);

            try
            {
                UpdateSetValueCall(property);
            }
            catch (Exception ex)
            {
                FodyEnvironment.WriteError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}");

#if DEBUG
                Debugger.Launch();
#endif
            }
        }
        public TypeReference GetCoreTypeReference(string typeName)
        {
            if (!_typeReferencesByFullName.ContainsKey(typeName))
            {
                var types = GetTypes();

                foreach (var type in types)
                {
                    _typeReferencesByFullName[type.FullName] = type;
                    _typeReferencesByShortName[type.Name]    = type;
                }
            }

            if (!_typeReferencesByFullName.TryGetValue(typeName, out var resolvedType))
            {
                if (!_typeReferencesByShortName.TryGetValue(typeName, out resolvedType))
                {
                    FodyEnvironment.WriteError($"Type '{typeName}' cannot be found, please report this bug");
                    return(null);
                }
            }

            return(resolvedType);
        }
Example #14
0
        private int AddSetValueCall(PropertyDefinition property, FieldReference fieldReference, bool isReadOnly)
        {
            FodyEnvironment.WriteDebug($"\t\t\t{property.Name} - adding SetValue call");

            //string fieldName = string.Format("{0}Property", property.Name);
            //var declaringType = property.DeclaringType;
            //var fieldReference = GetField(declaringType, fieldName);

            // Catel v5: SetValue<TValue>(PropertyData propertyName, TValue value)
            // Catel v6: SetValue<TValue>(IPropertyData propertyName, TValue value)

            if (property.SetMethod is null)
            {
                var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void");

                var setMethod = new MethodDefinition($"set_{property.Name}", MethodAttributes.Public, property.DeclaringType.Module.ImportReference(voidType));
                setMethod.Parameters.Add(new ParameterDefinition(property.PropertyType.Import()));

                property.DeclaringType.Methods.Add(setMethod);

                setMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder);

                property.SetMethod = setMethod;
            }

            var finalSetMethod = property.SetMethod;

            if (isReadOnly)
            {
                finalSetMethod.IsPrivate = true;
                finalSetMethod.IsPublic  = false;
            }

            var body = property.SetMethod.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            instructions.Clear();

            var instructionsToAdd = new List <Instruction>();

            instructionsToAdd.AddRange(new[]
            {
                Instruction.Create(OpCodes.Ldarg_0),
                Instruction.Create(OpCodes.Ldsfld, fieldReference),
                Instruction.Create(OpCodes.Ldarg_1)
            });

            // Check if Catel 5.12+ SetValue<TValue> is available
            var preferredSetValueInvoker = _catelType.PreferredSetValueInvoker;

            if (preferredSetValueInvoker.HasGenericParameters)
            {
                // Generic, no boxing required, but we need to make it generic
                var genericSetValue = new GenericInstanceMethod(preferredSetValueInvoker);

                foreach (var genericParameter in preferredSetValueInvoker.GenericParameters)
                {
                    genericSetValue.GenericParameters.Add(genericParameter);
                }

                genericSetValue.GenericArguments.Add(property.PropertyType.Import());

                preferredSetValueInvoker = genericSetValue;
            }
            else
            {
                // Non-generic, requires boxing
                if (property.PropertyType.IsBoxingRequired(_catelType.SetValueInvoker.Parameters[1].ParameterType))
                {
                    instructionsToAdd.Add(Instruction.Create(OpCodes.Box, property.PropertyType.Import()));
                }
            }

            if (preferredSetValueInvoker.Parameters.Count > 2)
            {
                // Catel v5 is a new signature:
                // protected internal void SetValue(string name, object value, bool notifyOnChange = true)
                instructionsToAdd.Add(Instruction.Create(OpCodes.Ldc_I4_1));
            }

            instructionsToAdd.Add(Instruction.Create(OpCodes.Call, preferredSetValueInvoker));
            instructionsToAdd.Add(Instruction.Create(OpCodes.Ret));

            var finalIndex = instructions.Insert(0, instructionsToAdd.ToArray());

            body.OptimizeMacros();

            return(finalIndex);
        }
Example #15
0
        public void Execute(bool force = false)
        {
            var preferredSetValueInvoker = _catelType.PreferredSetValueInvoker;

            if (_catelType.GetValueInvoker is null || preferredSetValueInvoker is null)
            {
                return;
            }

            var property = _propertyData.PropertyDefinition;

            if (property is null)
            {
                FodyEnvironment.WriteWarning("Skipping an unknown property because it has no property definition");
                return;
            }

            if (AlreadyContainsCallToMember(property.GetMethod, _catelType.GetValueInvoker.Name) ||
                AlreadyContainsCallToMember(property.SetMethod, preferredSetValueInvoker.Name))
            {
                FodyEnvironment.WriteDebug($"\t{property.GetName()} already has GetValue and/or SetValue functionality. Property will be ignored.");
                return;
            }

            if (!force && !HasBackingField(property))
            {
                FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it has no backing field");
                return;
            }

            if (ImplementsICommand(property))
            {
                FodyEnvironment.WriteDebug($"\t\tSkipping '{property.GetName()}' because it implements ICommand");
                return;
            }

            FodyEnvironment.WriteDebug("\t\t" + property.GetName());

            try
            {
                EnsureStaticConstructor(property.DeclaringType);

                AddChangeNotificationHandlerField(property, _propertyData);

                var fieldDefinition = AddPropertyFieldDefinition(property);
                if (!AddPropertyRegistration(property, _propertyData))
                {
                    return;
                }

                var fieldReference = GetFieldReference(property.DeclaringType, fieldDefinition.Name, true);

                AddGetValueCall(property, fieldReference);
                AddSetValueCall(property, fieldReference, _propertyData.IsReadOnly);

                RemoveBackingField(property);
            }
            catch (Exception ex)
            {
                FodyEnvironment.WriteError($"\t\tFailed to handle property '{property.DeclaringType.Name}.{property.Name}'\n{ex.Message}\n{ex.StackTrace}");

#if DEBUG
                Debugger.Launch();
#endif
            }
        }
Example #16
0
        private bool AddPropertyRegistration(PropertyDefinition property, CatelTypeProperty propertyData)
        {
            var fieldName      = $"{property.Name}Property";
            var declaringType  = property.DeclaringType;
            var fieldReference = GetFieldReference(declaringType, fieldName, true);

            if (fieldReference is null)
            {
                FodyEnvironment.WriteWarning($"\t\tCannot handle property '{_catelType.Name}.{property.Name}' because backing field is not found");
                return(false);
            }

            var staticConstructor = declaringType.Constructor(true);

            var body = staticConstructor.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            // Always inject before the first try block, or just before the last return statement
            var instructionToInsert = (from instruction in instructions
                                       where instruction.OpCode == OpCodes.Ret
                                       select instruction).FirstOrDefault();

            var exceptionHandler = body.ExceptionHandlers.FirstOrDefault();

            if (exceptionHandler != null)
            {
                instructionToInsert = exceptionHandler.TryStart;
            }

            var index = (instructionToInsert != null) ? instructions.IndexOf(instructionToInsert) : instructions.Count;

            var instructionsToInsert = new List <Instruction>();

            switch (_catelType.Version)
            {
            case CatelVersion.v5:
                instructionsToInsert.AddRange(CreatePropertyRegistration_Catel5(property, propertyData));
                break;

            case CatelVersion.v6:
                instructionsToInsert.AddRange(CreatePropertyRegistration_Catel6(property, propertyData));
                break;
            }

            var registerPropertyInvoker = (propertyData.DefaultValue is null) ? _catelType.RegisterPropertyWithoutDefaultValueInvoker : _catelType.RegisterPropertyWithDefaultValueInvoker;

            // Fill up the final booleans:
            // Catel v5: RegisterProperty([0] string name,
            //                            [1] Type type,
            //                            [2] Func<object> createDefaultValue = null,
            //                            [3] EventHandler<AdvancedPropertyChangedEventArgs> propertyChangedEventHandler = null,
            //                            [4] bool includeInSerialization = true,
            //                            [5] bool includeInBackup = true)
            //
            // Catel v6: RegisterProperty<TValue>([0] string name,
            //                                    [1] Func<TValue> createDefaultValue = null,
            //                                    [2] EventHandler<APropertyChangedEventArgs> propertyChangedEventHandler = null,
            //                                    [3] bool includeInSerialization = true,
            //                                    [4] bool includeInBackup = true)

            var parameters = registerPropertyInvoker.Parameters.Skip(3).ToList();
            var boolCount  = 0;

            for (var i = 0; i < parameters.Count; i++)
            {
                var parameter = parameters[i];
                if (string.CompareOrdinal(parameter.ParameterType.FullName, _moduleWeaver.TypeSystem.BooleanDefinition.FullName) != 0)
                {
                    continue;
                }

                boolCount++;

                // includeInBackup == 2nd bool value
                if (!propertyData.IncludeInBackup && boolCount == 2)
                {
                    instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_0));
                }
                else
                {
                    instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_1));
                }
            }

            // Make call to register property generic
            var finalRegisterPropertyMethod = registerPropertyInvoker;

            if (registerPropertyInvoker.HasGenericParameters)
            {
                var genericRegisterProperty = new GenericInstanceMethod(registerPropertyInvoker);
                if (registerPropertyInvoker.HasGenericParameters)
                {
                    foreach (var genericParameter in registerPropertyInvoker.GenericParameters)
                    {
                        genericRegisterProperty.GenericParameters.Add(genericParameter);
                    }

                    genericRegisterProperty.GenericArguments.Add(property.PropertyType.Import(false));
                }

                finalRegisterPropertyMethod = genericRegisterProperty;
            }

            instructionsToInsert.AddRange(new[]
            {
                Instruction.Create(OpCodes.Call, finalRegisterPropertyMethod),
                Instruction.Create(OpCodes.Stsfld, fieldReference)
            });

            instructions.Insert(index, instructionsToInsert);

            body.OptimizeMacros();

            return(true);
        }
Example #17
0
        public void Execute()
        {
            try
            {
#if DEBUG
                if (!Debugger.IsAttached)
                {
                    Debugger.Launch();

                    FodyEnvironment.LogDebug   = CreateLoggingCallback(LogDebug);
                    FodyEnvironment.LogInfo    = CreateLoggingCallback(LogInfo);
                    FodyEnvironment.LogWarning = CreateLoggingCallback(LogWarning);
                    FodyEnvironment.LogError   = CreateLoggingCallback(LogError);
                }
#endif

                // Clear cache because static members will be re-used over multiple builds over multiple systems
                CacheHelper.ClearAllCaches();

                var configuration = new Configuration(Config);

                InitializeEnvironment();

                LogInfo($"Catel.Fody v{GetType().Assembly.GetName().Version}");

                // 1st step: set up the basics
                var msCoreReferenceFinder = new MsCoreReferenceFinder(this, ModuleDefinition.AssemblyResolver);
                msCoreReferenceFinder.Execute();

                // Validate if Catel.Core is referenced
                var isRunningAgainstCatelCore = false;
                var catelCoreReference        = AssemblyResolver.Resolve("Catel.Core");
                if (catelCoreReference == null)
                {
                    if (!ModuleDefinition.Name.StartsWith("Catel.Core"))
                    {
                        LogWarning("No reference to Catel.Core found, this weaver is useless without referencing Catel");
                        return;
                    }

                    isRunningAgainstCatelCore = true;

                    LogInfo("No reference to Catel.Core found, but continuing because this is running against Catel.Core itself");
                }

                // Note: nested types not supported because we only list actual types (thus not nested)
                var types = ModuleDefinition.GetTypes().Where(x => x.IsClass && x.BaseType != null).ToList();

                var typeNodeBuilder = new CatelTypeNodeBuilder(types);
                typeNodeBuilder.Execute();

                // Remove any code generated types from the list of types to process
                var codeGenTypeCleaner = new CodeGenTypeCleaner(typeNodeBuilder);
                codeGenTypeCleaner.Execute();

                // 2nd step: Auto property weaving
                if (!isRunningAgainstCatelCore && configuration.WeaveProperties)
                {
                    FodyEnvironment.LogInfo("Weaving properties");

                    var propertyWeaverService = new AutoPropertiesWeaverService(configuration, typeNodeBuilder, msCoreReferenceFinder);
                    propertyWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.LogInfo("Weaving properties is disabled");
                }

                // 3rd step: Exposed properties weaving
                if (!isRunningAgainstCatelCore && configuration.WeaveExposedProperties)
                {
                    FodyEnvironment.LogInfo("Weaving exposed properties");

                    var exposedPropertiesWeaverService = new ExposedPropertiesWeaverService(typeNodeBuilder, msCoreReferenceFinder);
                    exposedPropertiesWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.LogInfo("Weaving exposed properties is disabled");
                }

                // 4th step: Argument weaving
                if (!isRunningAgainstCatelCore && configuration.WeaveArguments)
                {
                    FodyEnvironment.LogInfo("Weaving arguments");

                    var argumentWeaverService = new ArgumentWeaverService(types, msCoreReferenceFinder);
                    argumentWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.LogInfo("Weaving arguments is disabled");
                }

                // 5th step: Logging weaving (we will run this against Catel.Core)
                if (configuration.WeaveLogging)
                {
                    FodyEnvironment.LogInfo("Weaving logging");

                    var loggingWeaver = new LoggingWeaverService(types);
                    loggingWeaver.Execute();
                }
                else
                {
                    FodyEnvironment.LogInfo("Weaving logging is disabled");
                }

                // 6th step: Xml schema weaving
                if (!isRunningAgainstCatelCore && configuration.GenerateXmlSchemas)
                {
                    FodyEnvironment.LogInfo("Weaving xml schemas");

                    var xmlSchemasWeaverService = new XmlSchemasWeaverService(msCoreReferenceFinder, typeNodeBuilder);
                    xmlSchemasWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.LogInfo("Weaving xml schemas is disabled");
                }

                // Last step: clean up
                var referenceCleaner = new ReferenceCleaner(this);
                referenceCleaner.Execute();
            }
            catch (Exception ex)
            {
                LogError(ex.Message);

#if DEBUG
                if (!Debugger.IsAttached)
                {
                    Debugger.Launch();
                }
#endif
            }
        }
Example #18
0
        public override void Execute()
        {
            try
            {
//#if DEBUG
//                if (!Debugger.IsAttached)
//                {
//                    Debugger.Launch();
//                }
//#endif

                // First of all, set the assembly resolver
                if (AssemblyResolver is null)
                {
                    AssemblyResolver = ModuleDefinition.AssemblyResolver;
                }

                if (TypeSystem is null)
                {
                    var typeCache = new global::Fody.TypeCache(x => AssemblyResolver.Resolve(x));
                    typeCache.BuildAssembliesToScan(this);

                    TypeSystem = new global::Fody.TypeSystem(x => typeCache.FindType(x), ModuleDefinition);
                }

                // Clear cache because static members will be re-used over multiple builds over multiple systems
                CacheHelper.ClearAllCaches();

                var configuration = new Configuration(Config);

                InitializeEnvironment();

                WriteInfo($"Catel.Fody v{GetType().Assembly.GetName().Version}");

                // 1st step: set up the basics
                var msCoreReferenceFinder = new MsCoreReferenceFinder(this, ModuleDefinition.AssemblyResolver);
                msCoreReferenceFinder.Execute();

                // Validate if Catel.Core is referenced
                configuration.IsRunningAgainstCatel = false;

                if (ModuleDefinition.Name.StartsWith("Catel.Core") ||
                    ModuleDefinition.Name.StartsWith("Catel.MVVM") ||
                    ModuleDefinition.Name.StartsWith("Catel.Serialization.") ||
                    ModuleDefinition.Name.StartsWith("Catel.Tests"))
                {
                    configuration.IsRunningAgainstCatel = true;

                    WriteInfo("Running against Catel itself, most features will be disabled");
                }

                var catelCoreReference = AssemblyResolver.Resolve("Catel.Core");
                if (!configuration.IsRunningAgainstCatel && catelCoreReference is null)
                {
                    WriteWarning("No reference to Catel.Core found, this weaver is useless without referencing Catel");
                    return;
                }

                // Note: nested types not supported because we only list actual types (thus not nested)
                var types = ModuleDefinition.GetTypes().Where(x => x.IsClass && x.BaseType != null).ToList();

                var typeNodeBuilder = new CatelTypeNodeBuilder(types, msCoreReferenceFinder);
                typeNodeBuilder.Execute();

                // Remove any code generated types from the list of types to process
                var codeGenTypeCleaner = new CodeGenTypeCleaner(typeNodeBuilder);
                codeGenTypeCleaner.Execute();

                // 2nd step: Auto property weaving
                if (!configuration.IsRunningAgainstCatel && configuration.WeaveProperties)
                {
                    FodyEnvironment.WriteInfo("Weaving properties");

                    var propertyWeaverService = new AutoPropertiesWeaverService(configuration, this, typeNodeBuilder, msCoreReferenceFinder);
                    propertyWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.WriteInfo("Weaving properties is disabled");
                }

                // 3rd step: Exposed properties weaving
                if (!configuration.IsRunningAgainstCatel && configuration.WeaveExposedProperties)
                {
                    FodyEnvironment.WriteInfo("Weaving exposed properties");

                    var exposedPropertiesWeaverService = new ExposedPropertiesWeaverService(this, configuration, typeNodeBuilder, msCoreReferenceFinder);
                    exposedPropertiesWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.WriteInfo("Weaving exposed properties is disabled");
                }

                // 4th step: Argument weaving
                if (configuration.WeaveArguments)
                {
                    FodyEnvironment.WriteInfo("Weaving arguments");

                    var argumentWeaverService = new ArgumentWeaverService(types, msCoreReferenceFinder, configuration);
                    argumentWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.WriteInfo("Weaving arguments is disabled");
                }

                // 5th step: Logging weaving (we will run this against Catel.Core)
                if (configuration.WeaveLogging)
                {
                    FodyEnvironment.WriteInfo("Weaving logging");

                    var loggingWeaver = new LoggingWeaverService(types);
                    loggingWeaver.Execute();
                }
                else
                {
                    FodyEnvironment.WriteInfo("Weaving logging is disabled");
                }

                // 6th step: Xml schema weaving
                if (!configuration.IsRunningAgainstCatel && configuration.GenerateXmlSchemas)
                {
                    FodyEnvironment.WriteInfo("Weaving xml schemas");

                    var xmlSchemasWeaverService = new XmlSchemasWeaverService(this, msCoreReferenceFinder, typeNodeBuilder);
                    xmlSchemasWeaverService.Execute();
                }
                else
                {
                    FodyEnvironment.WriteInfo("Weaving xml schemas is disabled");
                }

                // Validate that nothing has been left out
                var validationService = new ValidationService(this);
                validationService.Validate();

                // Last step: clean up
                var referenceCleaner = new ReferenceCleaner(this);
                referenceCleaner.Execute();
            }
            catch (Exception ex)
            {
                WriteError(ex.Message);

#if DEBUG
                if (!Debugger.IsAttached)
                {
                    Debugger.Launch();
                }
#endif
            }
        }
Example #19
0
        private bool AddPropertyRegistration(PropertyDefinition property, CatelTypeProperty propertyData)
        {
            var fieldName      = $"{property.Name}Property";
            var declaringType  = property.DeclaringType;
            var fieldReference = GetFieldReference(declaringType, fieldName, true);

            if (fieldReference == null)
            {
                FodyEnvironment.LogWarning($"\t\tCannot handle property '{_catelType.Name}.{property.Name}' because backing field is not found");
                return(false);
            }

            var staticConstructor = declaringType.Constructor(true);

            var body = staticConstructor.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            // Always inject before the first try block, or just before the last return statement
            var instructionToInsert = (from instruction in instructions
                                       where instruction.OpCode == OpCodes.Ret
                                       select instruction).FirstOrDefault();

            var exceptionHandler = body.ExceptionHandlers.FirstOrDefault();

            if (exceptionHandler != null)
            {
                instructionToInsert = exceptionHandler.TryStart;
            }

            var index = (instructionToInsert != null) ? instructions.IndexOf(instructionToInsert) : instructions.Count;

            //L_0000: ldstr "FullName"
            //L_0005: ldtoken string // note that this is the property type
            //L_000a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
            //L_000f: ldnull
            //L_0010: ldnull
            //L_0011: ldc.i4.1
            //L_0012: call class [Catel.Core]Catel.Data.PropertyData [Catel.Core]Catel.Data.ModelBase::RegisterProperty(string, class [mscorlib]System.Type, class [mscorlib]System.Func`1<object>, class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>, bool)
            //L_0017: stsfld class [Catel.Core]Catel.Data.PropertyData Catel.Fody.TestAssembly.ViewModelBaseTest::FullNameProperty

            var getTypeFromHandle = property.Module.GetMethodAndImport("GetTypeFromHandle");

            var instructionsToInsert = new List <Instruction>();

            instructionsToInsert.AddRange(new[]
            {
                Instruction.Create(OpCodes.Ldstr, property.Name),
                Instruction.Create(OpCodes.Ldtoken, property.PropertyType.Import()),
                Instruction.Create(OpCodes.Call, getTypeFromHandle),
            });

            var resolvedPropertyType = propertyData.PropertyDefinition.PropertyType.Resolve();

            // Default value
            if (propertyData.DefaultValue is string)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldstr, (string)propertyData.DefaultValue));
            }
            else if (propertyData.DefaultValue is bool)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (bool)propertyData.DefaultValue ? 1 : 0));
            }
            else if (propertyData.DefaultValue is int)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)propertyData.DefaultValue));
            }
            else if (propertyData.DefaultValue is long)
            {
                if ((long)propertyData.DefaultValue <= int.MaxValue)
                {
                    // Note: don't use Ldc_I8 here, although it is a long
                    instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)(long)propertyData.DefaultValue));
                    instructionsToInsert.Add(Instruction.Create(OpCodes.Conv_I8));
                }
                else
                {
                    instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I8, (long)propertyData.DefaultValue));
                }
            }
            else if (propertyData.DefaultValue is float)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R4, (float)propertyData.DefaultValue));
            }
            else if (propertyData.DefaultValue is double)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R8, (double)propertyData.DefaultValue));
            }
            else if (resolvedPropertyType != null && resolvedPropertyType.IsEnum && propertyData.DefaultValue != null)
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)((CustomAttributeArgument)propertyData.DefaultValue).Value));
            }
            else
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull));
            }

            if (propertyData.ChangeCallbackReference != null)
            {
                //L_0040: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1
                //L_0045: brtrue.s L_005a
                //L_0047: ldnull
                //L_0048: ldftn void Catel.Fody.TestAssembly.ModelBaseTest::<.cctor>b__0(object, class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs)
                //L_004e: newobj instance void [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>::.ctor(object, native int)
                //L_0053: stsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1
                //L_0058: br.s L_005a
                //L_005a: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1

                var handlerFieldName            = GetChangeNotificationHandlerFieldName(property);
                var handlerConstructorFieldName = GetChangeNotificationHandlerConstructorName(property);

                var handlerField        = GetFieldReference(property.DeclaringType, handlerFieldName, true);
                var handlerConstructor  = GetMethodReference(property.DeclaringType, handlerConstructorFieldName, true);
                var handlerType         = GetEventHandlerAdvancedPropertyChangedEventArgs(property);
                var importedHandlerType = handlerType.Resolve();

                var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs");
                var handlerTypeConstructor = declaringType.Module.ImportReference(importedHandlerType.Constructor(false));
                var genericConstructor     = handlerTypeConstructor.MakeHostInstanceGeneric(declaringType.Module.ImportReference(advancedPropertyChangedEventArgsType));

                var finalInstruction = Instruction.Create(OpCodes.Ldsfld, handlerField);

                instructionsToInsert.AddRange(new[]
                {
                    Instruction.Create(OpCodes.Ldsfld, handlerField),
                    Instruction.Create(OpCodes.Brtrue_S, finalInstruction),
                    Instruction.Create(OpCodes.Ldnull),
                    Instruction.Create(OpCodes.Ldftn, handlerConstructor),
                    Instruction.Create(OpCodes.Newobj, genericConstructor),
                    Instruction.Create(OpCodes.Stsfld, handlerField),
                    Instruction.Create(OpCodes.Br_S, finalInstruction),
                    finalInstruction
                });
            }
            else
            {
                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull));
            }

            var registerPropertyInvoker = (propertyData.DefaultValue == null) ? _catelType.RegisterPropertyWithoutDefaultValueInvoker : _catelType.RegisterPropertyWithDefaultValueInvoker;

            var parameters = registerPropertyInvoker.Parameters.Reverse().ToList();

            for (int i = 0; i < parameters.Count; i++)
            {
                var parameterType = parameters[i];
                if (string.CompareOrdinal(parameterType.ParameterType.FullName, FodyEnvironment.ModuleDefinition.TypeSystem.Boolean.FullName) != 0)
                {
                    break;
                }

                instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_1));
            }

            // Make call to register property generic
            var finalRegisterPropertyMethod = registerPropertyInvoker;

            if (registerPropertyInvoker.HasGenericParameters)
            {
                var genericRegisterProperty = new GenericInstanceMethod(registerPropertyInvoker);
                if (registerPropertyInvoker.HasGenericParameters)
                {
                    foreach (var genericParameter in registerPropertyInvoker.GenericParameters)
                    {
                        genericRegisterProperty.GenericParameters.Add(genericParameter);
                    }

                    genericRegisterProperty.GenericArguments.Add(property.PropertyType.Import(true));
                }

                finalRegisterPropertyMethod = genericRegisterProperty;
            }

            instructionsToInsert.AddRange(new[]
            {
                Instruction.Create(OpCodes.Call, finalRegisterPropertyMethod),
                Instruction.Create(OpCodes.Stsfld, fieldReference)
            });

            instructions.Insert(index, instructionsToInsert);

            body.OptimizeMacros();

            return(true);
        }
Example #20
0
        private void RemoveBackingField(PropertyDefinition property)
        {
            var fieldName = GetBackingFieldName(property);

            var declaringType = property.DeclaringType;

            var field = GetFieldDefinition(declaringType, fieldName, false);

            if (field != null)
            {
                foreach (var ctor in declaringType.GetConstructors())
                {
                    var ctorBody = ctor.Body;

                    ctorBody.SimplifyMacros();

                    var instructions            = ctorBody.Instructions;
                    var validInstructionCounter = 0;

                    for (var i = 0; i < instructions.Count; i++)
                    {
                        var instruction = instructions[i];
                        if (instruction.IsOpCode(OpCodes.Nop))
                        {
                            continue;
                        }

                        validInstructionCounter++;

                        if (instruction.UsesField(field))
                        {
                            FodyEnvironment.WriteDebug($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Converting field usage to property usage to maintain compatibility with Catel generated properties.");

                            if (instruction.IsOpCode(OpCodes.Stfld))
                            {
                                // Setter
                                instruction.OpCode = OpCodes.Call;

                                // Note: make sure to support generic types
                                MethodReference setter = property.SetMethod;
                                if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType)
                                {
                                    setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                instruction.Operand = declaringType.Module.ImportReference(setter);

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                var baseIndex = ctor.FindBaseConstructorIndex();
                                if (baseIndex >= 0)
                                {
                                    // After a call to a ctor, a double nop is required
                                    var indexToInsert = baseIndex + 1;
                                    if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop))
                                    {
                                        indexToInsert++;

                                        if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop))
                                        {
                                            indexToInsert++;
                                        }
                                    }

                                    instructions.MoveInstructionsToPosition(i - 2, 3, indexToInsert);
                                }
                            }
                            else if (instruction.IsOpCode(OpCodes.Ldfld))
                            {
                                // Getter
                                instruction.OpCode = OpCodes.Call;

                                // Note: make sure to support generic types
                                MethodReference getter = property.GetMethod;
                                if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType)
                                {
                                    getter = getter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                instruction.Operand = declaringType.Module.ImportReference(getter);

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                var baseIndex = ctor.FindBaseConstructorIndex();
                                if (baseIndex >= 0)
                                {
                                    // After a call to a ctor, a double nop is required
                                    var indexToInsert = baseIndex + 1;
                                    if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop))
                                    {
                                        indexToInsert++;

                                        if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop))
                                        {
                                            indexToInsert++;
                                        }
                                    }

                                    instructions.MoveInstructionsToPosition(i - 2, 3, indexToInsert);
                                }
                            }
                            else if (instruction.IsOpCode(OpCodes.Ldflda))
                            {
                                // Probably setting a generic field value to a value by directly using an address. Since this was code like this:
                                //
                                // call instance !0 MyCompany.Models.Base.ItemsModel`1 < !T >::get_SelectedItem()
                                // initobj !T
                                //
                                // We need to generate code like this:
                                //
                                // ldloca.s local
                                // initobj !T
                                // ldloc.0
                                // call instance void Catel.Fody.TestAssembly.CSharp6_AutoPropertyInitializer_Generic_ExpectedCode`1 < !T >::set_SelectedItem(!0)

                                // Note: make sure to support generic types
                                MethodReference setter = property.SetMethod;
                                if (declaringType.MakeGenericIfRequired() is GenericInstanceType genericInstanceType)
                                {
                                    setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                var variable = new VariableDefinition(property.PropertyType.MakeGenericIfRequired());
                                ctorBody.Variables.Add(variable);
                                ctorBody.InitLocals = true;

                                var newInstructions = new List <Instruction>
                                {
                                    Instruction.Create(OpCodes.Ldloca, variable),
                                    instructions[i + 1], // Just copy this initobj !T instruction
                                    Instruction.Create(OpCodes.Ldloc, variable),
                                    Instruction.Create(OpCodes.Call, setter)
                                };

                                // Remove 3 instructions
                                // ldarg
                                // ldflda
                                // init T
                                instructions.RemoveAt(i);
                                instructions.RemoveAt(i);

                                if (instructions[i - 1].IsOpCode(OpCodes.Ldarg, OpCodes.Ldarg_0))
                                {
                                    newInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0));
                                    instructions.RemoveAt(i - 1);
                                }

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                var baseIndex = ctor.FindBaseConstructorIndex();
                                if (baseIndex >= 0)
                                {
                                    // After a call to a ctor, a double nop is required
                                    var indexToInsert = baseIndex + 1;
                                    if (instructions.IsNextInstructionOpCode(baseIndex, OpCodes.Nop))
                                    {
                                        indexToInsert++;

                                        if (instructions.IsNextInstructionOpCode(baseIndex + 1, OpCodes.Nop))
                                        {
                                            indexToInsert++;
                                        }
                                    }

                                    instructions.Insert(indexToInsert, newInstructions);
                                }
                                else
                                {
                                    FodyEnvironment.WriteError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. A rare condition occurred (no base ctor found), please contact support");
                                }
                            }
                            else
                            {
                                FodyEnvironment.WriteError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Tried to convert it to property usage, but OpCode '{instruction.OpCode}' is not supported. Please raise an issue.");
                            }
                        }
                    }

                    ctorBody.OptimizeMacros();
                    ctor.UpdateDebugInfo();
                }

                declaringType.Fields.Remove(field);
            }
        }
Example #21
0
        private int AddSetValueCall(PropertyDefinition property, FieldReference fieldReference, bool isReadOnly)
        {
            FodyEnvironment.LogDebug($"\t\t\t{property.Name} - adding SetValue call");

            //string fieldName = string.Format("{0}Property", property.Name);
            //var declaringType = property.DeclaringType;
            //var fieldReference = GetField(declaringType, fieldName);

            // Writes SetValue(PropertyData propertyName, object value)

            if (property.SetMethod == null)
            {
                var voidType = _msCoreReferenceFinder.GetCoreTypeReference("Void");

                var setMethod = new MethodDefinition($"set_{property.Name}", MethodAttributes.Public, property.DeclaringType.Module.ImportReference(voidType));
                setMethod.Parameters.Add(new ParameterDefinition(property.PropertyType.Import()));

                property.DeclaringType.Methods.Add(setMethod);

                setMethod.MarkAsCompilerGenerated(_msCoreReferenceFinder);

                property.SetMethod = setMethod;
            }

            var finalSetMethod = property.SetMethod;

            if (isReadOnly)
            {
                finalSetMethod.IsPrivate = true;
                finalSetMethod.IsPublic  = false;
            }

            var body = property.SetMethod.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            instructions.Clear();

            var instructionsToAdd = new List <Instruction>();

            instructionsToAdd.AddRange(new[]
            {
                Instruction.Create(OpCodes.Ldarg_0),
                Instruction.Create(OpCodes.Ldsfld, fieldReference),
                Instruction.Create(OpCodes.Ldarg_1)
            });

            if (property.PropertyType.IsBoxingRequired(_catelType.SetValueInvoker.Parameters[1].ParameterType))
            {
                instructionsToAdd.Add(Instruction.Create(OpCodes.Box, property.PropertyType.Import()));
            }

            if (_catelType.SetValueInvoker.Parameters.Count > 2)
            {
                // Catel v5 is a new signature:
                // protected internal void SetValue(string name, object value, bool notifyOnChange = true)
                instructionsToAdd.Add(Instruction.Create(OpCodes.Ldc_I4_1));
            }

            instructionsToAdd.Add(Instruction.Create(OpCodes.Call, _catelType.SetValueInvoker));
            instructionsToAdd.Add(Instruction.Create(OpCodes.Ret));

            var finalIndex = instructions.Insert(0, instructionsToAdd.ToArray());

            body.OptimizeMacros();

            return(finalIndex);
        }
        private void RemoveBackingField(PropertyDefinition property)
        {
            var fieldName = GetBackingFieldName(property);

            var declaringType = property.DeclaringType;

            var field = GetFieldDefinition(declaringType, fieldName, false);

            if (field != null)
            {
                foreach (var ctor in declaringType.GetConstructors())
                {
                    var ctorBody = ctor.Body;

                    ctorBody.SimplifyMacros();

                    var instructions            = ctorBody.Instructions;
                    var validInstructionCounter = 0;

                    for (int i = 0; i < instructions.Count; i++)
                    {
                        var instruction = instructions[i];

                        if (instruction.IsOpCode(OpCodes.Nop))
                        {
                            continue;
                        }

                        validInstructionCounter++;

                        if (instruction.UsesField(field))
                        {
                            FodyEnvironment.LogDebug($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Converting field usage to property usage to maintain compatibility with Catel generated properties.");

                            if (instruction.IsOpCode(OpCodes.Stfld))
                            {
                                // Setter
                                instruction.OpCode = OpCodes.Call;

                                // Note: make sure to support generic types
                                MethodReference setter = property.SetMethod;
                                var             genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType;
                                if (genericInstanceType != null)
                                {
                                    setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                instruction.Operand = declaringType.Module.Import(setter);

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                instructions.MoveInstructionsToEnd(i - 2, 3);
                            }
                            else if (instruction.IsOpCode(OpCodes.Ldfld))
                            {
                                // Getter
                                instruction.OpCode = OpCodes.Call;

                                // Note: make sure to support generic types
                                MethodReference getter = property.GetMethod;
                                var             genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType;
                                if (genericInstanceType != null)
                                {
                                    getter = getter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                instruction.Operand = declaringType.Module.Import(getter);

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                instructions.MoveInstructionsToEnd(i - 2, 3);
                            }
                            else if (instruction.IsOpCode(OpCodes.Ldflda))
                            {
                                // Probably setting a generic field value to a value by directly using an address. Since this was code like this:
                                //
                                // call instance !0 MyCompany.Models.Base.ItemsModel`1 < !T >::get_SelectedItem()
                                // initobj !T
                                //
                                // We need to generate code like this:
                                //
                                // ldloca.s local
                                // initobj !T
                                // ldloc.0
                                // call instance void Catel.Fody.TestAssembly.CSharp6_AutoPropertyInitializer_Generic_ExpectedCode`1 < !T >::set_SelectedItem(!0)

                                // Note: make sure to support generic types
                                MethodReference setter = property.SetMethod;
                                var             genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType;
                                if (genericInstanceType != null)
                                {
                                    setter = setter.MakeHostInstanceGeneric(genericInstanceType.GenericArguments.ToArray());
                                }

                                var variable = new VariableDefinition(property.PropertyType.MakeGenericIfRequired());
                                ctorBody.Variables.Add(variable);
                                ctorBody.InitLocals = true;

                                var newInstructions = new List <Instruction>();
                                newInstructions.Add(Instruction.Create(OpCodes.Ldloca, variable));
                                newInstructions.Add(instructions[i + 1]); // Just copy this instruction
                                newInstructions.Add(Instruction.Create(OpCodes.Ldloc, variable));
                                newInstructions.Add(Instruction.Create(OpCodes.Call, setter));

                                // Remove 2 instructions
                                instructions.RemoveAt(i);
                                instructions.RemoveAt(i);

                                // Now move this to the end of the method (we need to call the base ctor first to have the property bag ready)
                                instructions.Insert(instructions.Count - 1, newInstructions);
                            }
                            else
                            {
                                FodyEnvironment.LogError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. Tried to convert it to property usage, but OpCode '{instruction.OpCode}' is not supported. Please raise an issue.");
                            }
                        }
                    }

                    ctorBody.OptimizeMacros();
                }

                declaringType.Fields.Remove(field);
            }
        }