void ImplementStrategy(ImplementationStrategy strategy) { //Just in case... if (strategy.IsIgnored) { return; } if (strategy.Property.SetMethod.IsAbstract) { //This is an abstract property, we don't do these. throw new InvalidNotifierException(); } switch (strategy.ImplementationStyle) { case ImplementationStyle.Inline: ImplementInline(strategy); break; case ImplementationStyle.Wrapped: ImplementWrapped(strategy); break; default: throw new NotImplementedException(); } ContainsChanges = true; }
static void ImplementInline(ImplementationStrategy strategy) { var method = strategy.Property.SetMethod; var msil = method.Body.GetILProcessor(); var begin = method.Body.Instructions.First(); var end = method.Body.Instructions.Last(); if (strategy.NotificationStyle == NotificationStyle.OnSet) { InsertBefore(msil, msil.Create(OpCodes.Nop), begin); InsertBefore(msil, CallNotifyTargetInstructions(msil, strategy), end); } else { throw new BuildTaskErrorException("Inline implementation does not support OnChange notification"); } }
static IEnumerable <Instruction> CallNotifyTargetInstructions(ILProcessor ilProcessor, ImplementationStrategy strategy) { foreach (var name in strategy.NotifyValues) { yield return(ilProcessor.Create(OpCodes.Ldarg_0)); if (strategy.NotifyTarget.Parameters.Count > 0) { yield return(ilProcessor.Create(OpCodes.Ldstr, name)); } if (strategy.NotifyTarget.Parameters.Count > 1) { yield return(ilProcessor.Create(OpCodes.Ldarg_1)); if (strategy.Property.PropertyType.IsValueType) { yield return(ilProcessor.Create(OpCodes.Box, strategy.Property.PropertyType)); } } var opCode = strategy.NotifyTargetDefinition.IsVirtual ? OpCodes.Callvirt : OpCodes.Call; yield return(ilProcessor.Create(opCode, strategy.NotifyTarget)); yield return(ilProcessor.Create(OpCodes.Nop)); } }
static void ImplementWrapped(ImplementationStrategy strategy) { var setMethod = strategy.Property.SetMethod; var newMethod = DuplicateMethod(setMethod, $"{setMethod.Name}`mist"); var msil = setMethod.Body.GetILProcessor(); var instructions = new List <Instruction>(); var rtn = msil.Create(OpCodes.Ret); if (strategy.NotificationStyle == NotificationStyle.OnChange) { var boolType = strategy.Property.Module.ImportReference(typeof(bool)); var propertyType = strategy.Property.PropertyType.Resolve(); var equality = strategy.Property.Module.ImportReference(defaultEqualsMethod); var equalityReference = equality.Resolve(); var v1 = new VariableDefinition(strategy.Property.PropertyType); var v2 = new VariableDefinition(boolType); setMethod.Body.Variables.Add(v1); setMethod.Body.Variables.Add(v2); instructions.Add(msil.Create(OpCodes.Nop)); if (propertyType.IsValueType) { instructions.AddRange( new[] { msil.Create(OpCodes.Ldarg_0), msil.Create(OpCodes.Call, strategy.Property.GetMethod), msil.Create(OpCodes.Stloc_0), msil.Create(OpCodes.Ldarg_0), msil.Create(OpCodes.Ldarg_1), msil.Create(OpCodes.Call, newMethod), msil.Create(OpCodes.Ldloc_0), msil.Create(OpCodes.Box, v1.VariableType), msil.Create(OpCodes.Ldarg_1), msil.Create(OpCodes.Box, v1.VariableType), msil.Create(OpCodes.Call, equality), msil.Create(OpCodes.Ldc_I4_0), msil.Create(OpCodes.Ceq), msil.Create(OpCodes.Stloc_1), msil.Create(OpCodes.Ldloc_1), msil.Create(OpCodes.Brfalse_S, rtn), } ); } else { instructions.AddRange( new[] { msil.Create(OpCodes.Ldarg_0), msil.Create(OpCodes.Call, strategy.Property.GetMethod), msil.Create(OpCodes.Stloc_0), msil.Create(OpCodes.Ldarg_0), msil.Create(OpCodes.Ldarg_1), msil.Create(OpCodes.Call, newMethod), msil.Create(OpCodes.Ldloc_0), msil.Create(OpCodes.Ldarg_1), msil.Create(OpCodes.Call, equality), msil.Create(OpCodes.Ldc_I4_0), msil.Create(OpCodes.Ceq), msil.Create(OpCodes.Stloc_1), msil.Create(OpCodes.Ldloc_1), msil.Create(OpCodes.Brfalse_S, rtn) } ); } instructions.AddRange(CallNotifyTargetInstructions(msil, strategy)); instructions.Add(rtn); } else { instructions.AddRange(new[] { msil.Create(OpCodes.Ldarg_0), msil.Create(OpCodes.Ldarg_1), msil.Create(OpCodes.Call, newMethod), }); instructions.AddRange(CallNotifyTargetInstructions(msil, strategy)); instructions.Add(rtn); } setMethod.Body.Instructions.Clear(); foreach (var instruction in instructions) { setMethod.Body.Instructions.Add(instruction); } newMethod.DeclaringType.Methods.Add(newMethod); }