protected GenericInstanceType GetPropertyChangedEventHandler_Catel4_Catel5(PropertyDefinition property)
        {
            var genericHandlerType = _msCoreReferenceFinder.GetCoreTypeReference("System.EventHandler`1");

            if (genericHandlerType is null)
            {
                FodyEnvironment.WriteError("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 #2
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 #4
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 #5
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
            }
        }