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