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
string handlerFieldName = GetChangeNotificationHandlerFieldName(property);
string 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.Import(importedHandlerType.Constructor(false));
var genericConstructor = handlerTypeConstructor.MakeHostInstanceGeneric(declaringType.Module.Import(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;
}