public LType(ModuleDefinition module) { Type = module.FindType("L"); GetString = Type.Methods.Single(m => m.IsPublic && m.Parameters.Count == 2 && m.Parameters.All(p => p.ParameterType == module.TypeSystem.String) && m.ReturnType == module.FindType("LocString")); }
public CircuitEditorScreenType(ModuleDefinition module) { Type = module.FindType("GameLogic/CircuitEditorScreen"); // The Update() method is also defined on the IScreen interface, so get the name from there var updateMethod = module.FindType("IScreen").Methods.Single(m => m.Parameters.Count == 1 && m.Parameters[0].ParameterType == module.TypeSystem.Single); Update = Type.FindMethod(updateMethod.Name); }
public TracesType(ModuleDefinition module) { var structType = module.Types.Single(t => t.IsValueType && t.Fields.Count == 2 && t.Fields[0].FieldType == module.FindType("Index2") && t.Fields[1].FieldType == module.FindType("Trace")); // Traces is an IEnumerable of the above struct type Type = module.Types.Single(t => t.Interfaces.Count > 0 && t.Interfaces.Any(i => i.InterfaceType.ToString() == $"System.Collections.Generic.IEnumerable`1<{structType.Name}>")); GetSize = Type.Methods.Single(m => m.IsPublic && m.Parameters.Count == 0 && m.ReturnType == module.FindType("Index2")); }
public Index2Type(ModuleDefinition module) { Type = module.FindType("Index2"); Constructor = Type.FindMethod(".ctor"); X = Type.Fields[1]; Y = Type.Fields[2]; }
public SolutionType(ModuleDefinition module, TracesType traces) { Type = module.FindType("Solution"); Constructor = Type.FindMethod(".ctor"); PuzzleName = Type.Fields.Where(f => f.FieldType == module.TypeSystem.String).Skip(1).First(); Traces = Type.Fields.Single(f => f.FieldType == traces.Type); }
public MessageThreadsType(ModuleDefinition module) { Type = module.FindType("MessageThreads"); ClassConstructor = Type.FindMethod(".cctor"); CreateAllThreads = Type.Methods.Single(m => m.IsPublic && m.IsStatic && m.Body.Variables.Count == 2 && m.Body.Variables[0].VariableType == module.FindType("MessageThread")); CreateThread = Type.Methods.Single(m => m.IsPrivate && m.IsStatic && m.Parameters.Count == 8); AllThreads = Type.Fields.Single(f => f.FieldType.IsArray && f.FieldType.GetElementType() == module.FindType("MessageThread")); }
public PuzzleType(ModuleDefinition module) { Type = module.FindType("Puzzle"); Constructor = Type.FindMethod(".ctor"); Name = Type.Fields.First(f => f.FieldType == module.TypeSystem.String); IsSandbox = Type.Fields.First(f => f.FieldType == module.TypeSystem.Boolean); Terminals = Type.Fields.Single(f => f.FieldType.ToString() == "Terminal[]"); Tiles = Type.Fields.Single(f => f.FieldType.ToString() == "System.Int32[]"); GetTestRunOutputs = Type.Fields.Single(f => f.FieldType.ToString().StartsWith("System.Func`3")); }
public ClassEnhancerWeaver(WeaverContext context) : base(context) { injectAccessAttribute = ModuleDefinition.FindType("Someta", "InjectAccessAttribute"); injectTargetAttribute = ModuleDefinition.FindType("Someta", "InjectTargetAttribute"); }
public GameLogicType(ModuleDefinition module) { Type = module.FindType("GameLogic"); CircuitEditorScreen = new CircuitEditorScreenType(module); }
public void Execute() { LogInfo("ReactiveCommandObleak weaving"); // Validate we have the dependencies we need var mscorlib = ModuleDefinition.FindAssembly("mscorlib", LogError); if (mscorlib == null) { return; } var obleakCore = ModuleDefinition.FindAssembly("Obleak.Fody.Core", LogError); if (obleakCore == null) { return; } var reactiveUiCore = ModuleDefinition.FindAssembly("ReactiveUI", LogError); if (reactiveUiCore == null) { return; } // Get the IDisposable and IReactiveCommand types var disposableType = new TypeReference("System", "IDisposable", ModuleDefinition, mscorlib); var disposeableTypeResolved = ModuleDefinition.Import(disposableType).Resolve(); var disposeMethod = ModuleDefinition.Import(disposeableTypeResolved.Methods.First(x => x.Name == "Dispose")); var reactiveCommandType = new TypeReference("ReactiveUI", "IReactiveCommand", ModuleDefinition, reactiveUiCore); var obleakCommandAttribute = ModuleDefinition.FindType("Obleak.Fody.Core", "ObleakReactiveCommandAttribute", obleakCore); if (obleakCommandAttribute == null) { throw new Exception("obleakCommandAttribute is null"); } // Any class where the ObleakCommand attribute appears on a proeprty var targets = ModuleDefinition.GetAllTypes().Where(x => x.IsDefined(obleakCommandAttribute) || x.Properties.Any(y => y.IsDefined(obleakCommandAttribute))); targets.ForEach(target => { LogInfo($"Weaving target: {target.FullName}"); // We can only weave classes which are disposable if (!target.IsDisposable()) { LogError($"Target class {target.FullName} is not disposable and therefore cannot be weaved with Obleak Command"); return; } // Is this for all reactive commands in the class? var isClassWide = target.IsDefined(obleakCommandAttribute); // The command properties we're going to call dispose on var properties = target.Properties.Where(p => (isClassWide || p.IsDefined(obleakCommandAttribute)) && // That implements IReactiveCommand reactiveCommandType.IsAssignableFrom(p.PropertyType) && // And is disposable disposableType.IsAssignableFrom(p.PropertyType)); // If his type doesn't already have a dispose method create one var hasDisposeMethod = target.HasDisposeMethod(); if (!hasDisposeMethod && target.HasGenericParameters) { LogError($"Automatically generating a Dispose method for {target.Name} is not supported due to generics in it's inheritance hierarchy. " + $"You need to create an empty parameterless Dispose() and re-build to use the Obleak weavers"); return; } if (!hasDisposeMethod) { target.CreateDisposeMethod(); } // Find the dispose method and append the instructions at the end to clean up the composite disposable var dispose = target.GetDisposeMethod(); dispose.Body.Emit(il => { var last = dispose.Body.Instructions.Last(i => i.OpCode == OpCodes.Ret && (i.Next == null || dispose.Body.Instructions.Last() == i)); properties.ForEach(p => { var propertyGetMethod = p.GetMethod; // Now load this property, call the get method and then call dispose il.InsertBefore(last, il.Create(OpCodes.Ldarg_0)); // this il.InsertBefore(last, il.Create(OpCodes.Call, propertyGetMethod)); il.InsertBefore(last, il.Create(OpCodes.Callvirt, disposeMethod)); }); }); LogInfo($"Completed weaving target: {target.FullName}"); }); }
public TextureManagerType(ModuleDefinition module) { var textureType = module.FindType("Texture"); Type = module.Types.Single(t => t.Fields.Count > 24 && t.Fields.Take(10).All(f => f.FieldType == textureType)); }
public TerminalType(ModuleDefinition module) { Type = module.FindType("Terminal"); }
public void Execute() { LogInfo("Obleak weaving"); // Validate we have the dependencies we need var rxCore = ModuleDefinition.FindAssembly("System.Reactive.Core", LogError); if (rxCore == null) { return; } var mscorlib = ModuleDefinition.FindAssembly("mscorlib", LogError); if (mscorlib == null) { return; } var obleakCore = ModuleDefinition.FindAssembly("Obleak.Fody.Core", LogError); if (obleakCore == null) { return; } // Get the IDisposable type var disposableType = new TypeReference("System", "IDisposable", ModuleDefinition, mscorlib); // Get the CompositeDisposable type and required methods var compositeDisposableType = new TypeReference("System.Reactive.Disposables", "CompositeDisposable", ModuleDefinition, rxCore); var compositeDisposableTypeResolved = compositeDisposableType.Resolve(); if (compositeDisposableTypeResolved == null) { throw new Exception("compositeDisposableTypeResolved is null"); } var compositeDisposableCtor = ModuleDefinition.Import(compositeDisposableTypeResolved.Methods.Single(m => m.IsConstructor && !m.HasParameters)); var compositeDisposableDisposeMethod = ModuleDefinition.Import(compositeDisposableTypeResolved.Methods.Single(m => m.Name == "Dispose")); var obleakAttribute = ModuleDefinition.FindType("Obleak.Fody.Core", "ObleakSubscriptionAttribute", obleakCore); if (obleakAttribute == null) { throw new Exception("obleakAttribute is null"); } var disposableExtensions = new TypeReference("Obleak.Fody.Core", "Extensions", ModuleDefinition, obleakCore).Resolve(); if (disposableExtensions == null) { throw new Exception("disposableExtensions is null"); } var handleWithExtensionMethod = ModuleDefinition.Import(disposableExtensions.Methods.Single(x => x.Name == "HandleWith")); if (handleWithExtensionMethod == null) { throw new Exception("handleWithExtensionMethod is null"); } // Any class where the Obleak attribute appears on the class, a constructor or a method var targets = ModuleDefinition.GetAllTypes() .Where( x => x.IsDefined(obleakAttribute) || x.GetConstructors().Any(y => y.IsDefined(obleakAttribute)) || x.GetMethods().Any(y => y.IsDefined(obleakAttribute))); targets.ForEach(target => { LogInfo($"Weaving target: {target.FullName}"); // We can only weave classes which are disposable if (!target.IsDisposable()) { LogError($"Target class {target.FullName} is not disposable and therefore cannot be weaved with Obleak"); return; } // If this is class wide process every method, else only tackle what has been attributed. If the class + specific methods / constructors // have the attribute, still process everything // Note: we only need one composite disposable per target type var isClassWide = target.IsDefined(obleakAttribute); // The methods we're going to weave var methods = target.Methods.Where(x => isClassWide || x.IsDefined(obleakAttribute)); // Declare a field for the composite disposable var compositeDisposableField = new FieldDefinition(COMPOSITE_DISPOSABLE_FIELD_NAME + target.Name, FieldAttributes.Private, compositeDisposableType); target.Fields.Add(compositeDisposableField); // Initialise this in all of the constructors in this class (not it's inheritance hierarchy) -- hence target.Methods usage target.Methods.Where(m => m.IsConstructor).ForEach(constructor => { constructor.Body.Emit(il => { var first = constructor.Body.Instructions[0]; // first instruction // Instructions equivalent of: this.$ObleakCompositeDisposable = new CompositeDisposable(); // But as it's done as the first instruction set when decompiled this is actually // private CompositeDisposable $ObleakCompositeDisposable = new CompositeDisposable(); il.InsertBefore(first, il.Create(OpCodes.Ldarg_0)); // this il.InsertBefore(first, il.Create(OpCodes.Newobj, compositeDisposableCtor)); // new CompositeDisposable from ctor il.InsertBefore(first, il.Create(OpCodes.Stfld, compositeDisposableField.BindDefinition(target))); // store new obj in fld }); }); // For every .Subscribe() which returns a disposable call the .Add method on the compositeDisposableField methods.Where(m => m.HasBody && m.Body.Instructions.Any(i => _isExpectedMethodCall(i, disposableType, "Subscribe"))).ForEach(method => { method.Body.Emit(il => { var subscribes = method.Body.Instructions.Where(i => _isExpectedMethodCall(i, disposableType, "Subscribe")).ToArray(); subscribes.ForEach(i => { var next = i.Next; il.InsertBefore(next, il.Create(OpCodes.Ldarg_0)); // this il.InsertBefore(next, il.Create(OpCodes.Ldfld, compositeDisposableField.BindDefinition(target))); il.InsertBefore(next, il.Create(OpCodes.Call, handleWithExtensionMethod)); // Call .HandleWith($ObleakCompositeDisposable) }); }); }); // Does the target itself have a dispose method or is it inheriting the implementation? // If doesn't have one of it's own we need to add one and call the base.Dispose() as we need to clean up this // new local composite disposable. var hasDisposeMethod = target.HasDisposeMethod(); if (!hasDisposeMethod && target.HasGenericParameters) { LogError($"Automatically generating a Dispose method for {target.Name} is not supported due to generics in it's inheritance hierarchy. " + $"You need to create an empty parameterless Dispose() and re-build to use the Obleak weavers"); return; } if (!hasDisposeMethod) { target.CreateDisposeMethod(); } // Find the dispose method and append the instructions at the end to clean up the composite disposable var dispose = target.GetDisposeMethod(); dispose.Body.Emit(il => { var last = dispose.Body.Instructions.Last(i => i.OpCode == OpCodes.Ret && (i.Next == null || dispose.Body.Instructions.Last() == i)); il.InsertBefore(last, il.Create(OpCodes.Ldarg_0)); // this il.InsertBefore(last, il.Create(OpCodes.Ldfld, compositeDisposableField.BindDefinition(target))); il.InsertBefore(last, il.Create(OpCodes.Callvirt, compositeDisposableDisposeMethod)); // call $ObleakCompositeDisposable.Dispose(); }); LogInfo($"Completed weaving target: {target.FullName}"); }); }
public void Execute() { var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); if (reactiveUI == null) { LogInfo("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo($"{reactiveUI.Name} {reactiveUI.Version}"); var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); if (helpers == null) { LogInfo("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo($"{helpers.Name} {helpers.Version}"); var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI); var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType != null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray(); var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve(); if (reactiveObjectExtensions == null) { throw new Exception("reactiveObjectExtensions is null"); } var raiseAndSetIfChangedMethod = ModuleDefinition.Import(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaiseAndSetIfChanged")); if (raiseAndSetIfChangedMethod == null) { throw new Exception("raiseAndSetIfChangedMethod is null"); } var reactiveAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ReactiveAttribute", helpers); if (reactiveAttribute == null) { throw new Exception("reactiveAttribute is null"); } foreach (var targetType in targetTypes) { foreach (var property in targetType.Properties.Where(x => x.IsDefined(reactiveAttribute)).ToArray()) { if (property.SetMethod == null) { LogError($"Property {property.DeclaringType.FullName}.{property.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [Reactive]"); continue; } // Declare a field to store the property value var field = new FieldDefinition("$" + property.Name, FieldAttributes.Private, property.PropertyType); targetType.Fields.Add(field); // Remove old field (the generated backing field for the auto property) var oldField = (FieldReference)property.GetMethod.Body.Instructions.Where(x => x.Operand is FieldReference).Single().Operand; var oldFieldDefinition = oldField.Resolve(); targetType.Fields.Remove(oldFieldDefinition); // See if there exists an initializer for the auto-property var constructors = targetType.Methods.Where(x => x.IsConstructor); foreach (var constructor in constructors) { var fieldAssignment = constructor.Body.Instructions.SingleOrDefault(x => Equals(x.Operand, oldFieldDefinition) || Equals(x.Operand, oldField)); if (fieldAssignment != null) { // Replace field assignment with a property set (the stack semantics are the same for both, // so happily we don't have to manipulate the bytecode any further.) var setterCall = constructor.Body.GetILProcessor().Create(property.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, property.SetMethod); constructor.Body.GetILProcessor().Replace(fieldAssignment, setterCall); } } // Build out the getter which simply returns the value of the generated field property.GetMethod.Body = new MethodBody(property.GetMethod); property.GetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldfld, field.BindDefinition(targetType)); // pop -> this.$PropertyName il.Emit(OpCodes.Ret); // Return the field value that is lying on the stack }); TypeReference genericTargetType = targetType; if (targetType.HasGenericParameters) { var genericDeclaration = new GenericInstanceType(targetType); foreach (var parameter in targetType.GenericParameters) { genericDeclaration.GenericArguments.Add(parameter); } genericTargetType = genericDeclaration; } var methodReference = raiseAndSetIfChangedMethod.MakeGenericMethod(genericTargetType, property.PropertyType); // Build out the setter which fires the RaiseAndSetIfChanged method if (property.SetMethod == null) { throw new Exception("[Reactive] is decorating " + property.DeclaringType.FullName + "." + property.Name + ", but the property has no setter so there would be nothing to react to. Consider removing the attribute."); } property.SetMethod.Body = new MethodBody(property.SetMethod); property.SetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldflda, field.BindDefinition(targetType)); // pop -> this.$PropertyName il.Emit(OpCodes.Ldarg_1); // value il.Emit(OpCodes.Ldstr, property.Name); // "PropertyName" il.Emit(OpCodes.Call, methodReference); // pop * 4 -> this.RaiseAndSetIfChanged(this.$PropertyName, value, "PropertyName") il.Emit(OpCodes.Pop); // We don't care about the result of RaiseAndSetIfChanged, so pop it off the stack (stack is now empty) il.Emit(OpCodes.Ret); // Return out of the function }); } } }
/// <summary> /// Executes this instance. /// </summary> /// <exception cref="Exception"> /// reactiveObjectExtensions is null /// or /// raisePropertyChangedMethod is null /// or /// reactiveDecoratorAttribute is null. /// </exception> public void Execute() { if (ModuleDefinition is null) { LogInfo?.Invoke("The module definition has not been defined."); return; } var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); if (reactiveUI is null) { LogInfo?.Invoke("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo?.Invoke($"{reactiveUI.Name} {reactiveUI.Version}"); var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); if (helpers is null) { LogInfo?.Invoke("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo?.Invoke($"{helpers.Name} {helpers.Version}"); var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI); var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray(); var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve(); if (reactiveObjectExtensions is null) { throw new Exception("reactiveObjectExtensions is null"); } var raisePropertyChangedMethod = ModuleDefinition.ImportReference(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaisePropertyChanged")); if (raisePropertyChangedMethod is null) { throw new Exception("raisePropertyChangedMethod is null"); } var reactiveDependencyAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ReactiveDependencyAttribute", helpers); if (reactiveDependencyAttribute is null) { throw new Exception("reactiveDecoratorAttribute is null"); } foreach (var targetType in targetTypes.Where(x => x.Properties.Any(y => y.IsDefined(reactiveDependencyAttribute))).ToArray()) { foreach (var facadeProperty in targetType.Properties.Where(x => x.IsDefined(reactiveDependencyAttribute)).ToArray()) { // If the property already has a body then do not weave to prevent loss of instructions if (!facadeProperty.GetMethod.Body.Instructions.Any(x => x.Operand is FieldReference) || facadeProperty.GetMethod.Body.HasVariables) { LogError?.Invoke($"Property {facadeProperty.Name} is not an auto property and therefore not suitable for ReactiveDependency weaving"); continue; } var attribute = facadeProperty.CustomAttributes.First(x => x.AttributeType.FullName == reactiveDependencyAttribute.FullName); var targetNamedArgument = attribute.ConstructorArguments.FirstOrDefault(); var targetValue = targetNamedArgument.Value?.ToString(); if (string.IsNullOrEmpty(targetValue)) { LogError?.Invoke("No target property defined on the object"); continue; } if (targetType.Properties.All(x => x.Name != targetValue) && targetType.Fields.All(x => x.Name != targetValue)) { LogError?.Invoke($"dependency object property/field name '{targetValue}' not found on target type {targetType.Name}"); continue; } var objPropertyTarget = targetType.Properties.FirstOrDefault(x => x.Name == targetValue); var objFieldTarget = targetType.Fields.FirstOrDefault(x => x.Name == targetValue); var objDependencyTargetType = objPropertyTarget is not null ? objPropertyTarget.PropertyType.Resolve() : objFieldTarget?.FieldType.Resolve(); if (objDependencyTargetType is null) { LogError?.Invoke("Couldn't result the dependency type"); continue; } // Look for the target property on the member obj var destinationPropertyNamedArgument = attribute.Properties.FirstOrDefault(x => x.Name == "TargetProperty"); var destinationPropertyName = destinationPropertyNamedArgument.Argument.Value?.ToString(); // If no target property was specified use this property's name as the target on the decorated object (ala a decorated property) if (string.IsNullOrEmpty(destinationPropertyName)) { destinationPropertyName = facadeProperty.Name; } if (objDependencyTargetType.Properties.All(x => x.Name != destinationPropertyName)) { LogError?.Invoke($"Target property {destinationPropertyName} on dependency of type {objDependencyTargetType.DeclaringType.Name} not found"); continue; } var destinationProperty = objDependencyTargetType.Properties.First(x => x.Name == destinationPropertyName); // The property on the facade/decorator should have a setter if (facadeProperty.SetMethod is null) { LogError?.Invoke($"Property {facadeProperty.DeclaringType.FullName}.{facadeProperty.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [ReactiveDecorator]"); continue; } // The property on the dependency should have a setter e.g. Dependency.SomeProperty = value; if (destinationProperty.SetMethod is null) { LogError?.Invoke($"Dependency object's property {destinationProperty.DeclaringType.FullName}.{destinationProperty.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [ReactiveDecorator]"); continue; } // Remove old field (the generated backing field for the auto property) var oldField = (FieldReference)facadeProperty.GetMethod.Body.Instructions.Single(x => x.Operand is FieldReference).Operand; var oldFieldDefinition = oldField.Resolve(); targetType.Fields.Remove(oldFieldDefinition); // See if there exists an initializer for the auto-property var constructors = targetType.Methods.Where(x => x.IsConstructor); foreach (var constructor in constructors) { var fieldAssignment = constructor.Body.Instructions.SingleOrDefault(x => Equals(x.Operand, oldFieldDefinition) || Equals(x.Operand, oldField)); if (fieldAssignment is not null) { // Replace field assignment with a property set (the stack semantics are the same for both, // so happily we don't have to manipulate the byte code any further.) var setterCall = constructor.Body.GetILProcessor().Create(facadeProperty.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, facadeProperty.SetMethod); constructor.Body.GetILProcessor().Replace(fieldAssignment, setterCall); } } // Build out the getter which simply returns the value of the generated field facadeProperty.GetMethod.Body = new MethodBody(facadeProperty.GetMethod); facadeProperty.GetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldarg_0); if (objPropertyTarget is not null) { il.Emit(objPropertyTarget.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, objPropertyTarget.GetMethod); } else { il.Emit(OpCodes.Ldfld, objFieldTarget); } il.Emit(destinationProperty.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, destinationProperty.GetMethod); il.Emit(OpCodes.Ret); }); TypeReference genericTargetType = targetType; if (targetType.HasGenericParameters) { var genericDeclaration = new GenericInstanceType(targetType); foreach (var parameter in targetType.GenericParameters) { genericDeclaration.GenericArguments.Add(parameter); } genericTargetType = genericDeclaration; } var methodReference = raisePropertyChangedMethod.MakeGenericMethod(genericTargetType); facadeProperty.SetMethod.Body = new MethodBody(facadeProperty.SetMethod); facadeProperty.SetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldarg_0); if (objPropertyTarget is not null) { il.Emit(objPropertyTarget.GetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, objPropertyTarget.GetMethod); } else { il.Emit(OpCodes.Ldfld, objFieldTarget); } il.Emit(OpCodes.Ldarg_1); il.Emit(destinationProperty.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, destinationProperty.SetMethod); // Set the nested property il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, facadeProperty.Name); // "PropertyName" il.Emit(OpCodes.Call, methodReference); // this.RaisePropertyChanged("PropertyName") il.Emit(OpCodes.Ret); }); } } }
public void Execute() { var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "Reactive.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); var reactiveObject = new TypeReference("ReactiveUI", "IReactiveObject", ModuleDefinition, reactiveUI); var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType != null && reactiveObject.IsAssignableFrom(x.BaseType)).ToArray(); var reactivePropertyExtensions = ModuleDefinition.FindType("Reactive.Fody.Helpers", "ReactivePropertyExtensions", helpers).Resolve(); if (reactivePropertyExtensions == null) { throw new Exception("reactivePropertyExtensions is null"); } var raiseAndSetIfChangedMethod = ModuleDefinition.ImportReference(reactivePropertyExtensions.Methods.Single(x => x.Name == "RaiseAndSetIfChanged")); if (raiseAndSetIfChangedMethod == null) { throw new Exception("raiseAndSetIfChangedMethod is null"); } var reactiveAttribute = ModuleDefinition.FindType("Reactive.Fody.Helpers", "ReactiveAttribute", helpers); if (reactiveAttribute == null) { throw new Exception("reactiveAttribute is null"); } var reactiveObjectExtensions = new TypeReference("ReactiveUI", "IReactiveObjectExtensions", ModuleDefinition, reactiveUI).Resolve(); if (reactiveObjectExtensions == null) { throw new Exception("reactiveObjectExtensions is null"); } var raisePropertyChangedMethod = ModuleDefinition.ImportReference(reactiveObjectExtensions.Methods.Single(x => x.Name == "RaisePropertyChanged")); if (raisePropertyChangedMethod == null) { throw new Exception("raisePropertyChangedMethod is null"); } foreach (var targetType in targetTypes) { var setMethodByGetMethods = targetType.Properties .Where(x => x.SetMethod != null && x.GetMethod != null && x.IsDefined(reactiveAttribute)) .ToDictionary(x => x.GetMethod, x => x.SetMethod); foreach (var property in targetType.Properties.Where(x => x.IsDefined(reactiveAttribute)).ToArray()) { TypeReference genericTargetType = targetType; if (targetType.HasGenericParameters) { var genericDeclaration = new GenericInstanceType(targetType); foreach (var parameter in targetType.GenericParameters) { genericDeclaration.GenericArguments.Add(parameter); } genericTargetType = genericDeclaration; } MethodDefinition[] getMethods; if (property.SetMethod == null && property.GetMethod.TryGetMethodDependencies(out getMethods)) { var setMethodsForGetInstructions = getMethods .Where(x => setMethodByGetMethods.ContainsKey(x)) .Select(x => setMethodByGetMethods[x]) .ToArray(); if (!setMethodsForGetInstructions.Any()) { LogError($"Get only Property {property.DeclaringType.FullName}.{property.Name} has no supported dependent properties. " + $"Only dependent auto properties decorated with the [Reactive] attribute can be weaved to raise property change on {property.Name}"); } var raisePropertyChangedMethodReference = raisePropertyChangedMethod.MakeGenericMethod(genericTargetType); foreach (var method in setMethodsForGetInstructions) { method.Body.Emit(il => { var last = method.Body.Instructions.Last(i => i.OpCode == OpCodes.Ret); il.InsertBefore(last, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(last, il.Create(OpCodes.Ldstr, property.Name)); il.InsertBefore(last, il.Create(OpCodes.Call, raisePropertyChangedMethodReference)); }); } // Move on to next property for the target type continue; } if (property.SetMethod == null) { LogError($"Property {property.DeclaringType.FullName}.{property.Name} has no setter, therefore it is not possible for the property to change, and thus should not be marked with [Reactive]"); continue; } // Declare a field to store the property value var field = new FieldDefinition("$" + property.Name, FieldAttributes.Private, property.PropertyType); targetType.Fields.Add(field); // Remove old field (the generated backing field for the auto property) var oldField = (FieldReference)property.GetMethod.Body.Instructions.Where(x => x.Operand is FieldReference).Single().Operand; var oldFieldDefinition = oldField.Resolve(); targetType.Fields.Remove(oldFieldDefinition); // See if there exists an initializer for the auto-property var constructors = targetType.Methods.Where(x => x.IsConstructor); foreach (var constructor in constructors) { var fieldAssignment = constructor.Body.Instructions.SingleOrDefault(x => Equals(x.Operand, oldFieldDefinition) || Equals(x.Operand, oldField)); if (fieldAssignment != null) { // Replace field assignment with a property set (the stack semantics are the same for both, // so happily we don't have to manipulate the bytecode any further.) var setterCall = constructor.Body.GetILProcessor().Create(property.SetMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, property.SetMethod); constructor.Body.GetILProcessor().Replace(fieldAssignment, setterCall); } } // Build out the getter which simply returns the value of the generated field property.GetMethod.Body = new MethodBody(property.GetMethod); property.GetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldfld, field.BindDefinition(targetType)); // pop -> this.$PropertyName il.Emit(OpCodes.Ret); // Return the field value that is lying on the stack }); var methodReference = raiseAndSetIfChangedMethod.MakeGenericMethod(genericTargetType, property.PropertyType); // Build out the setter which fires the RaiseAndSetIfChanged method if (property.SetMethod == null) { throw new Exception("[Reactive] is decorating " + property.DeclaringType.FullName + "." + property.Name + ", but the property has no setter so there would be nothing to react to. Consider removing the attribute."); } property.SetMethod.Body = new MethodBody(property.SetMethod); property.SetMethod.Body.Emit(il => { RaiseAndSetIfChanged(methodReference, targetType, field, il, property.Name); }); } } }
public static MethodDefinition FindMethod(this ModuleDefinition module, string typeName, string methodName) { return(module.FindType(typeName).FindMethod(methodName)); }
public static FieldDefinition FindField(this ModuleDefinition module, string typeName, string fieldName) { return(module.FindType(typeName).FindField(fieldName)); }
public StateWeaver(WeaverContext context) : base(context) { // Debugger.Launch(); injectedFieldType = ModuleDefinition.FindType("Someta", "InjectedField`1", Context.Someta, "T"); injectFieldAttributeType = ModuleDefinition.FindType("Someta", "InjectFieldAttribute", Context.Someta); }
public void Execute() { var sexyProxy = ModuleDefinition.FindAssembly("SexyProxy"); if (sexyProxy == null) { LogError("Could not find assembly: SexyProxy (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } var proxyAttribute = ModuleDefinition.FindType("SexyProxy", "ProxyAttribute", sexyProxy); if (proxyAttribute == null) { throw new Exception($"{nameof(proxyAttribute)} is null"); } var proxyForAttribute = ModuleDefinition.FindType("SexyProxy", "ProxyForAttribute", sexyProxy); var doNotProxyAttribute = ModuleDefinition.FindType("SexyProxy", "DoNotProxyAttribute", sexyProxy); var originalMethodAttributeConstructor = ModuleDefinition.FindConstructor(ModuleDefinition.FindType("SexyProxy", "OriginalMethodAttribute", sexyProxy)); var reverseProxyInterface = ModuleDefinition.FindType("SexyProxy", "IReverseProxy", sexyProxy); var proxyInterface = ModuleDefinition.FindType("SexyProxy", "IProxy", sexyProxy); var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.IsDefined(proxyAttribute, true) || reverseProxyInterface.IsAssignableFrom(x) || proxyInterface.IsAssignableFrom(x)).ToArray(); // Get external proxy references var proxyFors = ModuleDefinition.Assembly.GetCustomAttributes(proxyForAttribute).Select(x => (TypeReference)x.ConstructorArguments.Single().Value).Select(x => x.Resolve()).ToArray(); targetTypes = targetTypes.Concat(proxyFors).ToArray(); var methodInfoType = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(MethodInfo).FullName)); var propertyInfoType = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(PropertyInfo).FullName)); var func2Type = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(Func <,>).FullName)); var action1Type = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(Action <>).FullName)); // for some reason this does not work: // var objectArrayType = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(object[]).FullName)); var objectArrayType = ModuleDefinition.Import(typeof(object[])); var taskType = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(Task).FullName)); var invocationTType = ModuleDefinition.FindType("SexyProxy", "InvocationT`1", sexyProxy, "T"); var asyncInvocationTType = ModuleDefinition.FindType("SexyProxy", "AsyncInvocationT`1", sexyProxy, "T"); var invocationHandlerType = ModuleDefinition.FindType("SexyProxy", "InvocationHandler", sexyProxy); var invocationHandlerIsHandlerActive = ModuleDefinition.FindMethod(invocationHandlerType, "IsHandlerActive"); var voidInvocationType = ModuleDefinition.FindType("SexyProxy", "VoidInvocation", sexyProxy); var voidInvocationConstructor = ModuleDefinition.FindConstructor(voidInvocationType); var voidAsyncInvocationType = ModuleDefinition.FindType("SexyProxy", "VoidAsyncInvocation", sexyProxy); var voidAsyncInvocationConstructor = ModuleDefinition.FindConstructor(voidAsyncInvocationType); var voidInvokeMethod = ModuleDefinition.FindMethod(invocationHandlerType, "VoidInvoke"); var asyncVoidInvokeMethod = ModuleDefinition.FindMethod(invocationHandlerType, "VoidAsyncInvoke"); var invokeTMethod = ModuleDefinition.FindMethod(invocationHandlerType, "InvokeT"); var asyncInvokeTMethod = ModuleDefinition.FindMethod(invocationHandlerType, "AsyncInvokeT"); var objectType = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(object).FullName)); var proxyGetInvocationHandlerMethod = ModuleDefinition.FindGetter(proxyInterface, "InvocationHandler"); var reverseProxyGetInvocationHandlerMethod = ModuleDefinition.FindGetter(reverseProxyInterface, "InvocationHandler"); var invocationType = ModuleDefinition.FindType("SexyProxy", "Invocation", sexyProxy); var invocationGetArguments = ModuleDefinition.FindGetter(invocationType, "Arguments"); var invocationGetProxy = ModuleDefinition.FindGetter(invocationType, "Proxy"); var asyncTaskMethodBuilder = ModuleDefinition.Import(ModuleWeaver.FindType(typeof(AsyncTaskMethodBuilder <>).FullName)); var methodFinder = ModuleDefinition.FindType("SexyProxy.Reflection", "MethodFinder`1", sexyProxy, "T"); var findMethod = ModuleDefinition.FindMethod(methodFinder, "FindMethod"); var findProperty = ModuleDefinition.FindMethod(methodFinder, "FindProperty"); var context = new WeaverContext { ModuleDefinition = ModuleDefinition, LogWarning = LogWarning, LogError = LogError, LogInfo = LogInfo, SexyProxy = sexyProxy, MethodInfoType = methodInfoType, PropertyInfoType = propertyInfoType, Action1Type = action1Type, AsyncInvocationTType = asyncInvocationTType, Func2Type = func2Type, InvocationTType = invocationTType, ObjectArrayType = objectArrayType, TaskType = taskType, AsyncInvokeTMethod = asyncInvokeTMethod, AsyncVoidInvokeMethod = asyncVoidInvokeMethod, InvocationHandlerType = invocationHandlerType, InvocationHandlerIsHandlerActive = invocationHandlerIsHandlerActive, InvokeTMethod = invokeTMethod, ObjectType = objectType, VoidAsyncInvocationConstructor = voidAsyncInvocationConstructor, VoidInvocationConstructor = voidInvocationConstructor, VoidInvokeMethod = voidInvokeMethod, ProxyGetInvocationHandlerMethod = proxyGetInvocationHandlerMethod, ReverseProxyGetInvocationHandlerMethod = reverseProxyGetInvocationHandlerMethod, InvocationType = invocationType, VoidInvocationType = voidInvocationType, VoidAsyncInvocationType = voidAsyncInvocationType, InvocationGetArguments = invocationGetArguments, InvocationGetProxy = invocationGetProxy, AsyncTaskMethodBuilder = asyncTaskMethodBuilder, MethodFinder = methodFinder, FindMethod = findMethod, FindProperty = findProperty, DoNotProxyAttribute = doNotProxyAttribute, OriginalMethodAttributeConstructor = originalMethodAttributeConstructor }; foreach (var sourceType in targetTypes) { LogInfo($"Emitting proxy for {sourceType.FullName}"); ClassWeaver classWeaver; if (sourceType.IsInterface) { classWeaver = new InterfaceClassWeaver(context, sourceType); } else if (proxyInterface.IsAssignableFrom(sourceType)) { classWeaver = new InPlaceClassWeaver(context, sourceType); } else if (reverseProxyInterface.IsAssignableFrom(sourceType)) { classWeaver = new ReverseProxyClassWeaver(context, sourceType); } else { classWeaver = new NonInterfaceClassWeaver(context, sourceType); } classWeaver.Execute(); } }
public PuzzlesType(ModuleDefinition module) { Type = module.FindType("Puzzles"); ClassConstructor = Type.FindMethod(".cctor"); FindPuzzle = Type.Methods.Single(m => m.Parameters.Count == 1 && m.Parameters[0].ParameterType == module.TypeSystem.String); }
public MessageThreadType(ModuleDefinition module) { Type = module.FindType("MessageThread"); Constructor = Type.FindMethod(".ctor"); }
public override void Execute() { // Debugger.Launch(); var soMeta = ModuleDefinition.FindAssembly("Someta"); CecilExtensions.LogInfo = LogInfo; CecilExtensions.LogWarning = LogWarning; CecilExtensions.LogError = LogError; CecilExtensions.Initialize(ModuleDefinition, TypeSystem, soMeta); var extensionPointInterface = ModuleDefinition.FindType("Someta", "IExtensionPoint", soMeta); var stateExtensionPointInterface = ModuleDefinition.FindType("Someta", "IStateExtensionPoint`1", soMeta, "T"); var stateExtensionPointInterfaceBase = ModuleDefinition.FindType("Someta", "IStateExtensionPoint", soMeta); var propertyGetInterceptorInterface = ModuleDefinition.FindType("Someta", "IPropertyGetInterceptor", soMeta); var propertySetInterceptorInterface = ModuleDefinition.FindType("Someta", "IPropertySetInterceptor", soMeta); var eventAddInterceptorInterface = ModuleDefinition.FindType("Someta", "IEventAddInterceptor", soMeta); var eventRemoveInterceptorInterface = ModuleDefinition.FindType("Someta", "IEventRemoveInterceptor", soMeta); var methodInterceptorInterface = ModuleDefinition.FindType("Someta", "IMethodInterceptor", soMeta); var asyncMethodInterceptorInterface = ModuleDefinition.FindType("Someta", "IAsyncMethodInterceptor", soMeta); var nonPublicAccessInterface = ModuleDefinition.FindType("Someta", "INonPublicAccess", soMeta); var asyncInvoker = ModuleDefinition.FindType("Someta.Helpers", "AsyncInvoker", soMeta); var asyncInvokerWrap = ModuleDefinition.FindMethod(asyncInvoker, "Wrap"); var asyncInvokerUnwrap = ModuleDefinition.FindMethod(asyncInvoker, "Unwrap"); var instanceInitializerInterfaceBase = ModuleDefinition.FindType("Someta", "IInstanceInitializer", soMeta); var instanceInitializerInterface = ModuleDefinition.FindType("Someta", "IInstanceInitializer`1", soMeta, "T"); // var interceptorScopeAttribute = ModuleDefinition.FindType("Someta", "InterceptorScopeAttribute", soMeta); // var requireScopeInterceptorInterface = ModuleDefinition.FindType("Someta", "IRequireScopeInterceptor", soMeta); var extensionPointScopesClass = ModuleDefinition.FindType("Someta", "ExtensionPointScopes", soMeta); var extensionPointScopesClassDefinition = extensionPointScopesClass.Resolve(); // var interceptorScopeInterface = interceptorScopesClassDefinition.NestedTypes.Single(x => x.Name == "Scope"); var extensionPointScopePropertyInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Property"); var extensionPointScopeMethodInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Method"); var extensionPointScopeClassInterface = extensionPointScopesClassDefinition.NestedTypes.Single(x => x.Name == "Class"); var propertyGetInterceptions = new List <(PropertyDefinition, ExtensionPointAttribute)>(); var propertySetInterceptions = new List <(PropertyDefinition, ExtensionPointAttribute)>(); var eventAddInterceptions = new List <(EventDefinition, ExtensionPointAttribute)>(); var eventRemoveInterceptions = new List <(EventDefinition, ExtensionPointAttribute)>(); var methodInterceptions = new List <(MethodDefinition, ExtensionPointAttribute)>(); var asyncMethodInterceptions = new List <(MethodDefinition, ExtensionPointAttribute)>(); var classEnhancers = new List <(TypeDefinition, ExtensionPointAttribute)>(); var stateInterceptions = new List <(IMemberDefinition, ExtensionPointAttribute)>(); var instanceInitializers = new List <(IMemberDefinition, ExtensionPointAttribute)>(); var propertyGetInterceptorWeaver = new PropertyGetInterceptorWeaver(CecilExtensions.Context, propertyGetInterceptorInterface); var propertySetInterceptorWeaver = new PropertySetInterceptorWeaver(CecilExtensions.Context, propertySetInterceptorInterface); var eventInterceptorWeaver = new EventInterceptorWeaver(CecilExtensions.Context); var methodInterceptorWeaver = new MethodInterceptorWeaver(CecilExtensions.Context, methodInterceptorInterface, asyncMethodInterceptorInterface); var asyncMethodInterceptorWeaver = new AsyncMethodInterceptorWeaver(CecilExtensions.Context, asyncMethodInterceptorInterface, asyncInvokerWrap, asyncInvokerUnwrap); var classEnhancerWeaver = new ClassEnhancerWeaver(CecilExtensions.Context); var stateWeaver = new StateWeaver(CecilExtensions.Context); var instanceInitializerWeaver = new InstanceInitializerWeaver(CecilExtensions.Context); // unscopedInterface: If present, and if genericTypes is empty (meaning no specific scope was specified), // unscopedInterface will be checked as a fallback. bool HasScope(ExtensionPointAttribute interceptor, TypeReference interfaceType, ExtensionPointScope scope, TypeReference unscopedInterface = null) { var genericTypes = interceptor.AttributeType.FindGenericInterfaces(interfaceType).ToArray(); foreach (var genericType in genericTypes) { var argument = genericType.GenericArguments[0]; TypeReference scopeInterface = null; switch (scope) { case ExtensionPointScope.Class: scopeInterface = extensionPointScopeClassInterface; break; case ExtensionPointScope.Property: scopeInterface = extensionPointScopePropertyInterface; break; case ExtensionPointScope.Method: scopeInterface = extensionPointScopeMethodInterface; break; } if (argument.CompareTo(scopeInterface)) { return(true); } } // If no scope was specified, we consider the scope satisfied if an unscoped version is satisfied // Furthermore, the scope must match the member type. var isScopeMatchedWithMember = interceptor.Scope == scope; return(unscopedInterface != null && genericTypes.Length == 0 && unscopedInterface.IsAssignableFrom(interceptor.AttributeType) && isScopeMatchedWithMember); } // Inventory candidate classes var allTypes = ModuleDefinition.GetAllTypes(); var assemblyInterceptorAttributes = ModuleDefinition.Assembly .GetCustomAttributesIncludingSubtypes(extensionPointInterface) .ToArray(); ExtensionPointAttribute[] assemblyInterceptors; // If we have any assembly-level interceptors, we create a special state class to hold the attribute instances (since attributes // can contain state, but getting attributes through reflection always returns a new instance. if (assemblyInterceptorAttributes.Any()) { // Debugger.Launch(); var assemblyState = new TypeDefinition("Someta", "AssemblyState", TypeAttributes.Public, TypeSystem.ObjectReference); ModuleDefinition.Types.Add(assemblyState); /* * var constructorWithTarget = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, TypeSystem.VoidReference); * constructorWithTarget.Body.Emit(il => * { * il.Emit(OpCodes.Ret); * }); * assemblyState.Methods.Add(constructorWithTarget); */ CecilExtensions.Context.AssemblyState = assemblyState; assemblyInterceptors = assemblyInterceptorAttributes .Select(x => new ExtensionPointAttribute(assemblyState, ModuleDefinition.Assembly, x, ModuleDefinition.Assembly.CustomAttributes.IndexOf(x), ExtensionPointScope.Assembly)) .ToArray(); foreach (var interceptor in assemblyInterceptors) { var fieldName = interceptor.AttributeType.FullName.Replace(".", "$"); var attributeField = new FieldDefinition(fieldName, FieldAttributes.Static | FieldAttributes.Public, interceptor.AttributeType); var index = ModuleDefinition.Assembly.CustomAttributes.IndexOf(interceptor.Attribute); assemblyState.Fields.Add(attributeField); assemblyState.EmitToStaticConstructor(il => { il.EmitGetAssemblyAttributeByIndex(index, interceptor.AttributeType); il.SaveField(attributeField); }); } } else { assemblyInterceptors = new ExtensionPointAttribute[0]; } // var moduleInterceptors = ModuleDefinition // .GetCustomAttributesIncludingSubtypes(extensionPointInterface) // .Select(x => new ExtensionPointAttribute(null, ModuleDefinition, x, ModuleDefinition.CustomAttributes.IndexOf(x), ExtensionPointScope.Module)) // .ToArray(); // var assemblyAndModuleInterceptors = assemblyInterceptors.Concat(moduleInterceptors).ToArray(); foreach (var type in allTypes) { // We can get into recursion scenarios if we allow extension points on extension points. For now, let's naively prohibit this if (extensionPointInterface.IsAssignableFrom(type)) { continue; } var classInterceptors = type .GetCustomAttributesInAncestry(extensionPointInterface) .Select(x => new ExtensionPointAttribute(x.DeclaringType, x.DeclaringType, x.Attribute, x.DeclaringType.CustomAttributes.IndexOf(x.Attribute), ExtensionPointScope.Class)) .Concat(assemblyInterceptors /*.Select(x => new ExtensionPointAttribute(type, x.DeclaringMember, x.Attribute, x.Index, x.Scope))*/) .ToArray(); foreach (var classInterceptor in classInterceptors) { LogInfo($"Found class interceptor {classInterceptor.AttributeType}"); if (nonPublicAccessInterface.IsAssignableFrom(classInterceptor.AttributeType)) { LogInfo($"Discovered class enhancer {classInterceptor.AttributeType.FullName} at {type.FullName}"); classEnhancers.Add((type, classInterceptor)); } if (HasScope(classInterceptor, stateExtensionPointInterface, ExtensionPointScope.Class, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered class state interceptor {classInterceptor.AttributeType.FullName} at {type.FullName}"); stateInterceptions.Add((type, classInterceptor)); } if (HasScope(classInterceptor, instanceInitializerInterface, ExtensionPointScope.Class, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {classInterceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((type, classInterceptor)); } } foreach (var property in type.Properties) { var interceptors = property.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, property, x, property.CustomAttributes.IndexOf(x), ExtensionPointScope.Property)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (propertyGetInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered property get interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); propertyGetInterceptions.Add((property, interceptor)); } if (propertySetInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered property set interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); propertySetInterceptions.Add((property, interceptor)); } if (HasScope(interceptor, stateExtensionPointInterface, ExtensionPointScope.Property, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered property state interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{property.Name}"); stateInterceptions.Add((property, interceptor)); } if (HasScope(interceptor, instanceInitializerInterface, ExtensionPointScope.Property, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {interceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((property, interceptor)); } } } foreach (var @event in type.Events) { // Debugger.Launch(); var interceptors = @event.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, @event, x, @event.CustomAttributes.IndexOf(x), ExtensionPointScope.Event)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (eventAddInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered event add interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{@event.Name}"); eventAddInterceptions.Add((@event, interceptor)); } if (eventRemoveInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered event remove interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{@event.Name}"); eventRemoveInterceptions.Add((@event, interceptor)); } } } foreach (var method in type.Methods.Where(x => !x.IsConstructor)) { var interceptors = method.GetCustomAttributesIncludingSubtypes(extensionPointInterface) .Select(x => new ExtensionPointAttribute(type, method, x, method.CustomAttributes.IndexOf(x), ExtensionPointScope.Method)) .Concat(classInterceptors); foreach (var interceptor in interceptors) { if (methodInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered method interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); methodInterceptions.Add((method, interceptor)); } if (asyncMethodInterceptorInterface.IsAssignableFrom(interceptor.AttributeType)) { LogInfo($"Discovered async method interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); asyncMethodInterceptions.Add((method, interceptor)); } if (HasScope(interceptor, stateExtensionPointInterface, ExtensionPointScope.Method, stateExtensionPointInterfaceBase)) { LogInfo($"Discovered method state interceptor {interceptor.AttributeType.FullName} at {type.FullName}.{method.Name}"); stateInterceptions.Add((method, interceptor)); } if (HasScope(interceptor, instanceInitializerInterface, ExtensionPointScope.Method, instanceInitializerInterfaceBase)) { LogInfo($"Discovered instance initializer {interceptor.AttributeType.FullName} at {type.FullName}"); instanceInitializers.Add((method, interceptor)); } } } } foreach (var(property, interceptor) in propertyGetInterceptions) { propertyGetInterceptorWeaver.Weave(property, interceptor); } foreach (var(property, interceptor) in propertySetInterceptions) { propertySetInterceptorWeaver.Weave(property, interceptor); } foreach (var(@event, interceptor) in eventAddInterceptions) { eventInterceptorWeaver.Weave(@event, interceptor, isAdd: true); } foreach (var(@event, interceptor) in eventRemoveInterceptions) { eventInterceptorWeaver.Weave(@event, interceptor, isAdd: false); } foreach (var(method, interceptor) in methodInterceptions) { methodInterceptorWeaver.Weave(method, interceptor); } foreach (var(method, interceptor) in asyncMethodInterceptions) { asyncMethodInterceptorWeaver.Weave(method, interceptor); } foreach (var(type, interceptor) in classEnhancers) { classEnhancerWeaver.Weave(type, interceptor); } foreach (var(member, interceptor) in stateInterceptions) { stateWeaver.Weave(member, interceptor); } foreach (var(type, interceptor) in instanceInitializers) { instanceInitializerWeaver.Weave(type, interceptor); } }
/// <summary> /// Executes this property weaver. /// </summary> public void Execute() { if (ModuleDefinition is null) { LogInfo?.Invoke("The module definition has not been defined."); return; } var reactiveUI = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI").OrderByDescending(x => x.Version).FirstOrDefault(); if (reactiveUI is null) { LogInfo?.Invoke("Could not find assembly: ReactiveUI (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo?.Invoke($"{reactiveUI.Name} {reactiveUI.Version}"); var helpers = ModuleDefinition.AssemblyReferences.Where(x => x.Name == "ReactiveUI.Fody.Helpers").OrderByDescending(x => x.Version).FirstOrDefault(); if (helpers is null) { LogInfo?.Invoke("Could not find assembly: ReactiveUI.Fody.Helpers (" + string.Join(", ", ModuleDefinition.AssemblyReferences.Select(x => x.Name)) + ")"); return; } LogInfo?.Invoke($"{helpers.Name} {helpers.Version}"); var exceptionName = typeof(Exception).FullName; if (exceptionName is null) { LogInfo?.Invoke("Could not find the full name for System.Exception"); return; } var reactiveObject = ModuleDefinition.FindType("ReactiveUI", "ReactiveObject", reactiveUI); // The types we will scan are subclasses of ReactiveObject var targetTypes = ModuleDefinition.GetAllTypes().Where(x => x.BaseType is not null && reactiveObject.IsAssignableFrom(x.BaseType)); var observableAsPropertyHelper = ModuleDefinition.FindType("ReactiveUI", "ObservableAsPropertyHelper`1", reactiveUI, "T"); var observableAsPropertyAttribute = ModuleDefinition.FindType("ReactiveUI.Fody.Helpers", "ObservableAsPropertyAttribute", helpers); var observableAsPropertyHelperGetValue = ModuleDefinition.ImportReference(observableAsPropertyHelper.Resolve().Properties.Single(x => x.Name == "Value").GetMethod); var exceptionDefinition = FindType?.Invoke(exceptionName); var constructorDefinition = exceptionDefinition.GetConstructors().Single(x => x.Parameters.Count == 1); var exceptionConstructor = ModuleDefinition.ImportReference(constructorDefinition); foreach (var targetType in targetTypes) { foreach (var property in targetType.Properties.Where(x => x.IsDefined(observableAsPropertyAttribute) || (x.GetMethod?.IsDefined(observableAsPropertyAttribute) ?? false)).ToArray()) { var genericObservableAsPropertyHelper = observableAsPropertyHelper.MakeGenericInstanceType(property.PropertyType); var genericObservableAsPropertyHelperGetValue = observableAsPropertyHelperGetValue.Bind(genericObservableAsPropertyHelper); ModuleDefinition.ImportReference(genericObservableAsPropertyHelperGetValue); // Declare a field to store the property value var field = new FieldDefinition("$" + property.Name, FieldAttributes.Private, genericObservableAsPropertyHelper); targetType.Fields.Add(field); // It's an auto-property, so remove the generated field if (property.SetMethod is not null && property.SetMethod.HasBody) { // Remove old field (the generated backing field for the auto property) var oldField = (FieldReference)property.GetMethod.Body.Instructions.Single(x => x.Operand is FieldReference).Operand; var oldFieldDefinition = oldField.Resolve(); targetType.Fields.Remove(oldFieldDefinition); // Re-implement setter to throw an exception property.SetMethod.Body = new MethodBody(property.SetMethod); property.SetMethod.Body.Emit(il => { il.Emit(OpCodes.Ldstr, "Never call the setter of an ObservableAsPropertyHelper property."); il.Emit(OpCodes.Newobj, exceptionConstructor); il.Emit(OpCodes.Throw); il.Emit(OpCodes.Ret); }); } property.GetMethod.Body = new MethodBody(property.GetMethod); property.GetMethod.Body.Emit(il => { var isValid = il.Create(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldfld, field.BindDefinition(targetType)); // pop -> this.$PropertyName il.Emit(OpCodes.Dup); // Put an extra copy of this.$PropertyName onto the stack il.Emit(OpCodes.Brtrue, isValid); // If the helper is null, return the default value for the property il.Emit(OpCodes.Pop); // Drop this.$PropertyName EmitDefaultValue(property.GetMethod.Body, il, property.PropertyType); // Put the default value onto the stack il.Emit(OpCodes.Ret); // Return that default value il.Append(isValid); // Add a marker for if the helper is not null il.Emit(OpCodes.Callvirt, genericObservableAsPropertyHelperGetValue); // pop -> this.$PropertyName.Value il.Emit(OpCodes.Ret); // Return the value that is on the stack }); } } }
private static TypeReference ImportOne(ModuleDefinition target, ModuleDefinition source, string name, params string[] namespaces) => namespaces .Select(n => source.FindType(n + "." + name)) .First(t => t != null) .ImportInto(target);