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);
}
}