Beispiel #1
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);
            }
        }
Beispiel #2
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}");
            });
        }
        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}");
            });
        }
Beispiel #4
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();
            }
        }