Exemple #1
0
 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"));
 }
Exemple #2
0
            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);
            }
Exemple #3
0
            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"));
            }
Exemple #4
0
 public Index2Type(ModuleDefinition module)
 {
     Type        = module.FindType("Index2");
     Constructor = Type.FindMethod(".ctor");
     X           = Type.Fields[1];
     Y           = Type.Fields[2];
 }
Exemple #5
0
 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);
 }
Exemple #6
0
 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"));
 }
Exemple #7
0
 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"));
 }
Exemple #8
0
 public ClassEnhancerWeaver(WeaverContext context) : base(context)
 {
     injectAccessAttribute = ModuleDefinition.FindType("Someta", "InjectAccessAttribute");
     injectTargetAttribute = ModuleDefinition.FindType("Someta", "InjectTargetAttribute");
 }
Exemple #9
0
 public GameLogicType(ModuleDefinition module)
 {
     Type = module.FindType("GameLogic");
     CircuitEditorScreen = new CircuitEditorScreenType(module);
 }
Exemple #10
0
        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}");
            });
        }
Exemple #11
0
            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));
            }
Exemple #12
0
 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);
                    });
                }
            }
        }
Exemple #16
0
        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);
                    });
                }
            }
        }
Exemple #17
0
 public static MethodDefinition FindMethod(this ModuleDefinition module, string typeName, string methodName)
 {
     return(module.FindType(typeName).FindMethod(methodName));
 }
Exemple #18
0
 public static FieldDefinition FindField(this ModuleDefinition module, string typeName, string fieldName)
 {
     return(module.FindType(typeName).FindField(fieldName));
 }
Exemple #19
0
        public StateWeaver(WeaverContext context) : base(context)
        {
//            Debugger.Launch();
            injectedFieldType        = ModuleDefinition.FindType("Someta", "InjectedField`1", Context.Someta, "T");
            injectFieldAttributeType = ModuleDefinition.FindType("Someta", "InjectFieldAttribute", Context.Someta);
        }
Exemple #20
0
        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();
            }
        }
Exemple #21
0
 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);
 }
Exemple #22
0
 public MessageThreadType(ModuleDefinition module)
 {
     Type        = module.FindType("MessageThread");
     Constructor = Type.FindMethod(".ctor");
 }
Exemple #23
0
        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);
            }
        }
Exemple #24
0
    /// <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
                });
            }
        }
    }
Exemple #25
0
 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);