public CatelType(TypeDefinition typeDefinition) { Mappings = new List <MemberMapping>(); Properties = new List <CatelTypeProperty>(); Ignore = true; TypeDefinition = typeDefinition; Name = typeDefinition.FullName; DetermineCatelType(); if (Type == CatelTypeType.Unknown) { FodyEnvironment.LogWarning($"Cannot determine the Catel type used for '{Name}', type will be ignored for weaving"); return; } try { DetermineTypes(); if (!DetermineMethods()) { return; } Properties = DetermineProperties(); DetermineMappings(); Ignore = false; } catch (Exception) { FodyEnvironment.LogWarning($"Failed to get additional information about type '{Name}', type will be ignored for weaving"); } }
private void DetermineMethods() { string methodName = $"On{PropertyDefinition.Name}Changed"; var declaringType = PropertyDefinition.DeclaringType; var callbackReferences = (from method in declaringType.Methods where method.Name == methodName select method).ToList(); foreach (var callbackReference in callbackReferences) { if (callbackReference != null) { if (callbackReference.HasParameters) { FodyEnvironment.LogWarning($"Method '{declaringType.FullName}.{callbackReference.Name}' matches automatic change method name but has parameters and will not be used as automatic change callback. Rename the method to remove this warning or remove parameters to use as automatic callback method."); continue; } MethodReference finalCallbackReference = callbackReference; if (declaringType.HasGenericParameters) { finalCallbackReference = finalCallbackReference.MakeGeneric(declaringType); } ChangeCallbackReference = finalCallbackReference; break; } } }
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 bool AddPropertyRegistration(PropertyDefinition property, CatelTypeProperty propertyData) { var fieldName = $"{property.Name}Property"; var declaringType = property.DeclaringType; var fieldReference = GetFieldReference(declaringType, fieldName, true); if (fieldReference == null) { FodyEnvironment.LogWarning($"\t\tCannot handle property '{_catelType.Name}.{property.Name}' because backing field is not found"); return(false); } var staticConstructor = declaringType.Constructor(true); var body = staticConstructor.Body; body.SimplifyMacros(); var instructions = body.Instructions; // Always inject before the first try block, or just before the last return statement var instructionToInsert = (from instruction in instructions where instruction.OpCode == OpCodes.Ret select instruction).FirstOrDefault(); var exceptionHandler = body.ExceptionHandlers.FirstOrDefault(); if (exceptionHandler != null) { instructionToInsert = exceptionHandler.TryStart; } var index = (instructionToInsert != null) ? instructions.IndexOf(instructionToInsert) : instructions.Count; //L_0000: ldstr "FullName" //L_0005: ldtoken string // note that this is the property type //L_000a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) //L_000f: ldnull //L_0010: ldnull //L_0011: ldc.i4.1 //L_0012: call class [Catel.Core]Catel.Data.PropertyData [Catel.Core]Catel.Data.ModelBase::RegisterProperty(string, class [mscorlib]System.Type, class [mscorlib]System.Func`1<object>, class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>, bool) //L_0017: stsfld class [Catel.Core]Catel.Data.PropertyData Catel.Fody.TestAssembly.ViewModelBaseTest::FullNameProperty var getTypeFromHandle = property.Module.GetMethodAndImport("GetTypeFromHandle"); var instructionsToInsert = new List <Instruction>(); instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Ldstr, property.Name), Instruction.Create(OpCodes.Ldtoken, property.PropertyType.Import()), Instruction.Create(OpCodes.Call, getTypeFromHandle), }); var resolvedPropertyType = propertyData.PropertyDefinition.PropertyType.Resolve(); // Default value if (propertyData.DefaultValue is string) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldstr, (string)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is bool) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (bool)propertyData.DefaultValue ? 1 : 0)); } else if (propertyData.DefaultValue is int) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is long) { if ((long)propertyData.DefaultValue <= int.MaxValue) { // Note: don't use Ldc_I8 here, although it is a long instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)(long)propertyData.DefaultValue)); instructionsToInsert.Add(Instruction.Create(OpCodes.Conv_I8)); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I8, (long)propertyData.DefaultValue)); } } else if (propertyData.DefaultValue is float) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R4, (float)propertyData.DefaultValue)); } else if (propertyData.DefaultValue is double) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_R8, (double)propertyData.DefaultValue)); } else if (resolvedPropertyType != null && resolvedPropertyType.IsEnum && propertyData.DefaultValue != null) { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4, (int)((CustomAttributeArgument)propertyData.DefaultValue).Value)); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull)); } if (propertyData.ChangeCallbackReference != null) { //L_0040: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 //L_0045: brtrue.s L_005a //L_0047: ldnull //L_0048: ldftn void Catel.Fody.TestAssembly.ModelBaseTest::<.cctor>b__0(object, class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs) //L_004e: newobj instance void [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs>::.ctor(object, native int) //L_0053: stsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 //L_0058: br.s L_005a //L_005a: ldsfld class [mscorlib]System.EventHandler`1<class [Catel.Core]Catel.Data.AdvancedPropertyChangedEventArgs> Catel.Fody.TestAssembly.ModelBaseTest::CS$<>9__CachedAnonymousMethodDelegate1 var handlerFieldName = GetChangeNotificationHandlerFieldName(property); var handlerConstructorFieldName = GetChangeNotificationHandlerConstructorName(property); var handlerField = GetFieldReference(property.DeclaringType, handlerFieldName, true); var handlerConstructor = GetMethodReference(property.DeclaringType, handlerConstructorFieldName, true); var handlerType = GetEventHandlerAdvancedPropertyChangedEventArgs(property); var importedHandlerType = handlerType.Resolve(); var advancedPropertyChangedEventArgsType = property.Module.FindType("Catel.Core", "Catel.Data.AdvancedPropertyChangedEventArgs"); var handlerTypeConstructor = declaringType.Module.ImportReference(importedHandlerType.Constructor(false)); var genericConstructor = handlerTypeConstructor.MakeHostInstanceGeneric(declaringType.Module.ImportReference(advancedPropertyChangedEventArgsType)); var finalInstruction = Instruction.Create(OpCodes.Ldsfld, handlerField); instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Ldsfld, handlerField), Instruction.Create(OpCodes.Brtrue_S, finalInstruction), Instruction.Create(OpCodes.Ldnull), Instruction.Create(OpCodes.Ldftn, handlerConstructor), Instruction.Create(OpCodes.Newobj, genericConstructor), Instruction.Create(OpCodes.Stsfld, handlerField), Instruction.Create(OpCodes.Br_S, finalInstruction), finalInstruction }); } else { instructionsToInsert.Add(Instruction.Create(OpCodes.Ldnull)); } var registerPropertyInvoker = (propertyData.DefaultValue == null) ? _catelType.RegisterPropertyWithoutDefaultValueInvoker : _catelType.RegisterPropertyWithDefaultValueInvoker; var parameters = registerPropertyInvoker.Parameters.Reverse().ToList(); for (int i = 0; i < parameters.Count; i++) { var parameterType = parameters[i]; if (string.CompareOrdinal(parameterType.ParameterType.FullName, FodyEnvironment.ModuleDefinition.TypeSystem.Boolean.FullName) != 0) { break; } instructionsToInsert.Add(Instruction.Create(OpCodes.Ldc_I4_1)); } // Make call to register property generic var finalRegisterPropertyMethod = registerPropertyInvoker; if (registerPropertyInvoker.HasGenericParameters) { var genericRegisterProperty = new GenericInstanceMethod(registerPropertyInvoker); if (registerPropertyInvoker.HasGenericParameters) { foreach (var genericParameter in registerPropertyInvoker.GenericParameters) { genericRegisterProperty.GenericParameters.Add(genericParameter); } genericRegisterProperty.GenericArguments.Add(property.PropertyType.Import(true)); } finalRegisterPropertyMethod = genericRegisterProperty; } instructionsToInsert.AddRange(new[] { Instruction.Create(OpCodes.Call, finalRegisterPropertyMethod), Instruction.Create(OpCodes.Stsfld, fieldReference) }); instructions.Insert(index, instructionsToInsert); body.OptimizeMacros(); return(true); }