Catel.Fody.CatelPropertyWeaver.RemoveBackingField C# (CSharp) Method

RemoveBackingField() private method

private RemoveBackingField ( Mono.Cecil.PropertyDefinition property ) : void
property Mono.Cecil.PropertyDefinition
return void
        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);
            }
        }