public EventInvokerMethod AddOnPropertyChangingMethod(TypeDefinition targetType) { var propertyChangingField = FindPropertyChangingField(targetType); if (propertyChangingField == null) { return(null); } if (FoundInterceptor) { if (targetType.HasGenericParameters) { var message = string.Format("Error processing '{0}'. Interception is not supported on generic types. To manually work around this problem add a [DoNotNotify] to the class and then manually implement INotifyPropertyChanging for that class and all child classes. If you would like this feature handled automatically please feel free to submit a pull request.", targetType.Name); throw new WeavingException(message); } var methodDefinition = GetMethodDefinition(targetType, propertyChangingField); return(new EventInvokerMethod { MethodReference = InjectInterceptedMethod(targetType, methodDefinition).GetGeneric(), IsVisibleFromChildren = true, InvokerType = InterceptorType, }); } return(new EventInvokerMethod { MethodReference = InjectMethod(targetType, EventInvokerNames.First(), propertyChangingField).GetGeneric(), IsVisibleFromChildren = true, InvokerType = InterceptorType, }); }
MethodDefinition FindEventInvokerMethodDefinition(TypeDefinition type) { var methodDefinition = type.Methods .Where(x => (x.IsFamily || x.IsFamilyAndAssembly || x.IsPublic || x.IsFamilyOrAssembly) && EventInvokerNames.Contains(x.Name)) .OrderByDescending(GetInvokerPriority) .FirstOrDefault(IsEventInvokerMethod); if (methodDefinition != null) { return(methodDefinition); } methodDefinition = type.Methods .Where(x => EventInvokerNames.Contains(x.Name)) .OrderByDescending(GetInvokerPriority) .FirstOrDefault(IsEventInvokerMethod); if (methodDefinition != null && methodDefinition.IsPrivate && methodDefinition.IsFinal && methodDefinition.IsVirtual && methodDefinition.Overrides.Count == 1) { // Explicitly implemented interfaces should call the interface method instead return(methodDefinition.Overrides[0].Resolve()); } return(methodDefinition); }
MethodReference FindExplicitImplementation(TypeDefinition type) { return(type.GetAllInterfaces() .Select(i => i.Resolve()) .Where(i => i != null && i.IsPublic) .SelectMany(i => i.Methods.Where(m => EventInvokerNames.Contains($"{i.FullName}.{m.Name}"))) .OrderByDescending(GetInvokerPriority) .FirstOrDefault(IsEventInvokerMethod)); }
MethodDefinition GetMethodDefinition(TypeDefinition targetType, FieldReference propertyChangedField) { var eventInvokerName = "Inner" + EventInvokerNames.First(); var methodDefinition = targetType.Methods.FirstOrDefault(x => x.Name == eventInvokerName); if (methodDefinition?.Parameters.Count == 1 && methodDefinition.Parameters[0].ParameterType.FullName == "System.String") { return(methodDefinition); } return(InjectMethod(targetType, eventInvokerName, propertyChangedField)); }
OnChangedMethod CreateOnChangedMethod(TypeNode notifyNode, MethodDefinition methodDefinition, bool isDefaultMethod) { if (methodDefinition.IsStatic) { if (!SuppressOnPropertyNameChangedWarning) { EmitConditionalWarning(methodDefinition, $"The type {notifyNode.TypeDefinition.FullName} has a On_PropertyName_Changed method ({methodDefinition.Name}) which is static."); } return(null); } if (methodDefinition.ReturnType.FullName != "System.Void") { if (!SuppressOnPropertyNameChangedWarning) { EmitConditionalWarning(methodDefinition, $"The type {notifyNode.TypeDefinition.FullName} has a On_PropertyName_Changed method ({methodDefinition.Name}) that has a non void return value. Ensure the return type void."); } return(null); } var typeDefinitions = new Stack <TypeDefinition>(); typeDefinitions.Push(notifyNode.TypeDefinition); var onChangedType = OnChangedTypes.None; if (IsNoArgOnChangedMethod(methodDefinition)) { onChangedType = OnChangedTypes.NoArg; } else if (IsBeforeAfterOnChangedMethod(methodDefinition)) { onChangedType = OnChangedTypes.BeforeAfter; } if (onChangedType != OnChangedTypes.None) { ValidateOnChangedMethod(notifyNode, methodDefinition, isDefaultMethod); return(new OnChangedMethod { OnChangedType = onChangedType, MethodReference = GetMethodReference(typeDefinitions, methodDefinition), IsDefaultMethod = isDefaultMethod }); } if (!EventInvokerNames.Contains(methodDefinition.Name) && !SuppressOnPropertyNameChangedWarning) { EmitConditionalWarning(methodDefinition, $"Unsupported signature for a On_PropertyName_Changed method: {methodDefinition.Name} in {methodDefinition.DeclaringType.FullName}"); } return(null); }
MethodDefinition GetMethodDefinition(TypeDefinition targetType) { var eventInvokerName = $"Inner{EventInvokerNames.First()}"; var methodDefinition = targetType.Methods.FirstOrDefault(x => x.Name == eventInvokerName); if (methodDefinition?.Parameters.Count == 1 && methodDefinition.Parameters[0].ParameterType.FullName == "System.String") { return(methodDefinition); } return(InjectMethod(targetType, eventInvokerName)); }
IEnumerable <OnChangedMethod> GetOnChangedMethods(TypeNode notifyNode) { var methods = notifyNode.TypeDefinition.Methods; var onChangedMethods = methods.Where(x => x.Name.StartsWith("On") && x.Name.EndsWith("Changed") && x.Name != "OnChanged"); foreach (var methodDefinition in onChangedMethods) { if (methodDefinition.IsStatic) { var message = $"The type {notifyNode.TypeDefinition.FullName} has a On_PropertyName_Changed method ({methodDefinition.Name}) which is static."; throw new WeavingException(message); } if (methodDefinition.ReturnType.FullName != "System.Void") { var message = $"The type {notifyNode.TypeDefinition.FullName} has a On_PropertyName_Changed method ({methodDefinition.Name}) that has a non void return value. Ensure the return type void."; throw new WeavingException(message); } var typeDefinitions = new Stack <TypeDefinition>(); typeDefinitions.Push(notifyNode.TypeDefinition); if (IsNoArgOnChangedMethod(methodDefinition)) { ValidateOnChangedMethod(notifyNode, methodDefinition); yield return(new OnChangedMethod { OnChangedType = OnChangedTypes.NoArg, MethodReference = GetMethodReference(typeDefinitions, methodDefinition) }); } else if (IsBeforeAfterOnChangedMethod(methodDefinition)) { ValidateOnChangedMethod(notifyNode, methodDefinition); yield return(new OnChangedMethod { OnChangedType = OnChangedTypes.BeforeAfter, MethodReference = GetMethodReference(typeDefinitions, methodDefinition) }); } else if (!EventInvokerNames.Contains(methodDefinition.Name)) { EmitConditionalWarning(methodDefinition, $"Unsupported signature for a On_PropertyName_Changed method: {methodDefinition.Name} in {methodDefinition.DeclaringType.FullName}"); } } }
bool FindEventInvokerMethodDefinition(TypeDefinition type, out MethodDefinition methodDefinition) { methodDefinition = type.Methods .Where(x => (x.IsFamily || x.IsFamilyAndAssembly || x.IsPublic || x.IsFamilyOrAssembly) && EventInvokerNames.Contains(x.Name)) .OrderByDescending(definition => definition.Parameters.Count) .FirstOrDefault(x => IsBeforeAfterGenericMethod(x) || IsBeforeAfterMethod(x) || IsSingleStringMethod(x) || IsPropertyChangedArgMethod(x) || IsSenderPropertyChangedArgMethod(x)); if (methodDefinition == null) { methodDefinition = type.Methods .Where(x => EventInvokerNames.Contains(x.Name)) .OrderByDescending(definition => definition.Parameters.Count) .FirstOrDefault(x => IsBeforeAfterGenericMethod(x) || IsBeforeAfterMethod(x) || IsSingleStringMethod(x) || IsPropertyChangedArgMethod(x) || IsSenderPropertyChangedArgMethod(x)); } return(methodDefinition != null); }
bool FindEventInvokerMethodDefinition(TypeDefinition type, out MethodDefinition methodDefinition) { methodDefinition = type.Methods .Where(x => (x.IsFamily || x.IsFamilyAndAssembly || x.IsPublic || x.IsFamilyOrAssembly) && EventInvokerNames.Contains(x.Name)) .OrderByDescending(definition => definition.Parameters.Count) .FirstOrDefault(x => IsBeforeAfterMethod(x) || IsSingleStringMethod(x) || IsPropertyChangedArgMethod(x)); if (methodDefinition == null) { //TODO: when injecting calls to this method should check visibility methodDefinition = type.Methods .Where(x => EventInvokerNames.Contains(x.Name)) .OrderByDescending(definition => definition.Parameters.Count) .FirstOrDefault(x => IsBeforeAfterMethod(x) || IsSingleStringMethod(x) || IsPropertyChangedArgMethod(x)); } return(methodDefinition != null); }
MethodDefinition FindEventInvokerMethodDefinition(TypeDefinition type) { var methodDefinition = type.Methods .Where(x => (x.IsFamily || x.IsFamilyAndAssembly || x.IsPublic || x.IsFamilyOrAssembly) && EventInvokerNames.Contains(x.Name)) .OrderByDescending(GetInvokerPriority) .FirstOrDefault(IsEventInvokerMethod); if (methodDefinition == null) { methodDefinition = type.Methods .Where(x => EventInvokerNames.Contains(x.Name)) .OrderByDescending(GetInvokerPriority) .FirstOrDefault(IsEventInvokerMethod); } return(methodDefinition); }
MethodDefinition InjectInterceptedMethod(TypeDefinition targetType, MethodDefinition innerOnPropertyChanging) { var delegateHolderInjector = new DelegateHolderInjector { TargetTypeDefinition = targetType, OnPropertyChangingMethodReference = innerOnPropertyChanging, ModuleWeaver = this }; delegateHolderInjector.InjectDelegateHolder(); var method = new MethodDefinition(EventInvokerNames.First(), GetMethodAttributes(targetType), ModuleDefinition.TypeSystem.Void); var propertyName = new ParameterDefinition("propertyName", ParameterAttributes.None, ModuleDefinition.TypeSystem.String); method.Parameters.Add(propertyName); if (InterceptorType == InvokerTypes.Before) { var before = new ParameterDefinition("before", ParameterAttributes.None, ModuleDefinition.TypeSystem.Object); method.Parameters.Add(before); } var action = new VariableDefinition("firePropertyChanging", ActionTypeReference); method.Body.Variables.Add(action); var variableDefinition = new VariableDefinition("delegateHolder", delegateHolderInjector.TypeDefinition); method.Body.Variables.Add(variableDefinition); var instructions = method.Body.Instructions; var last = Instruction.Create(OpCodes.Ret); instructions.Add(Instruction.Create(OpCodes.Newobj, delegateHolderInjector.ConstructorDefinition)); instructions.Add(Instruction.Create(OpCodes.Stloc_1)); instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); instructions.Add(Instruction.Create(OpCodes.Stfld, delegateHolderInjector.PropertyName)); instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); instructions.Add(Instruction.Create(OpCodes.Stfld, delegateHolderInjector.Target)); instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); instructions.Add(Instruction.Create(OpCodes.Ldftn, delegateHolderInjector.MethodDefinition)); instructions.Add(Instruction.Create(OpCodes.Newobj, ActionConstructorReference)); instructions.Add(Instruction.Create(OpCodes.Stloc_0)); instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); instructions.Add(Instruction.Create(OpCodes.Ldloc_0)); instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); instructions.Add(Instruction.Create(OpCodes.Ldfld, delegateHolderInjector.PropertyName)); if (InterceptorType == InvokerTypes.Before) { instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); instructions.Add(Instruction.Create(OpCodes.Call, InterceptMethod)); } else { instructions.Add(Instruction.Create(OpCodes.Call, InterceptMethod)); } instructions.Add(last); method.Body.InitLocals = true; targetType.Methods.Add(method); return(method); }