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 } }
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); }
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.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; var genericInstanceType = declaringType.MakeGenericIfRequired() as GenericInstanceType; if (genericInstanceType != null) { 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; 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 initobj !T instruction newInstructions.Add(Instruction.Create(OpCodes.Ldloc, variable)); newInstructions.Add(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.LogError($"Field '{declaringType.FullName}.{field.Name}' is used in ctor '{ctor}'. A rare condition occurred (no base ctor found), please contact support"); } } 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(); ctor.UpdateDebugInfo(); } declaringType.Fields.Remove(field); } }