Esempio n. 1
0
        protected override bool WeaveAssembly()
        {
            try
            {
                Logger.Debug($"Starting AutoDI Weaver v{GetType().Assembly.GetCustomAttribute<AssemblyVersionAttribute>()?.Version}", DebugLogLevel.Default);

                var typeResolver = new TypeResolver(ModuleDefinition, ModuleDefinition.AssemblyResolver, Logger);

                Settings settings = LoadSettings();
                if (settings == null)
                {
                    return(false);
                }

                ICollection <TypeDefinition> allTypes = typeResolver.GetAllTypes(settings, out AssemblyDefinition autoDIAssembly);

                Logger.Debug($"Found types:\r\n{string.Join("\r\n", allTypes.Select(x => x.FullName))}", DebugLogLevel.Verbose);

                if (autoDIAssembly == null)
                {
                    autoDIAssembly = ResolveAssembly("AutoDI");
                    if (autoDIAssembly == null)
                    {
                        Logger.Warning("Could not find AutoDI assembly");
                        return(false);
                    }
                    else
                    {
                        Logger.Warning($"Failed to find AutoDI assembly. Manually injecting '{autoDIAssembly.MainModule.FileName}'");
                    }
                }

                LoadRequiredData();

                ICodeGenerator gen = GetCodeGenerator(settings);

                if (settings.GenerateRegistrations)
                {
                    Mapping mapping = Mapping.GetMapping(settings, allTypes, Logger);

                    Logger.Debug($"Found potential map:\r\n{mapping}", DebugLogLevel.Verbose);

                    ModuleDefinition.Types.Add(GenerateAutoDIClass(mapping, settings, gen, out MethodDefinition initMethod));

                    switch (settings.InitMode)
                    {
                    case InitMode.None:
                        Logger.Debug("Skipping injections of Init method", DebugLogLevel.Verbose);
                        break;

                    case InitMode.EntryPoint:
                        InjectInitCall(initMethod);
                        break;

                    case InitMode.ModuleLoad:
                        InjectModuleCctorInitCall(initMethod);
                        break;

                    default:
                        Logger.Warning($"Unsupported InitMode: {settings.InitMode}");
                        break;
                    }
                }
                else
                {
                    Logger.Debug("Skipping registration", DebugLogLevel.Verbose);
                }

                //We only update types in our module
                foreach (TypeDefinition type in allTypes.Where(type => type.Module == ModuleDefinition))
                {
                    ProcessType(type, gen);
                }
                gen?.Save();
                return(true);
            }
            catch (Exception ex)
            {
                var sb = new StringBuilder();
                for (Exception e = ex; e != null; e = e.InnerException)
                {
                    sb.AppendLine(e.ToString());
                }
                Logger.Error(sb.ToString());
                return(false);
            }
        }
        private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings settings, TypeDefinition containerType, ICodeGenerator codeGenerator)
        {
            var method = new MethodDefinition("AddServices",
                                              MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static,
                                              Import.System.Void);

            var serviceCollection = new ParameterDefinition("collection", ParameterAttributes.None, Import.DependencyInjection.IServiceCollection);

            method.Parameters.Add(serviceCollection);

            IMethodGenerator methodGenerator = codeGenerator?.Method(method);
            ILProcessor      processor       = method.Body.GetILProcessor();

            VariableDefinition exceptionList = null;
            VariableDefinition exception     = null;

            if (settings.DebugExceptions)
            {
                var genericType = ModuleDefinition.ImportReference(Import.System.Collections.List.Type.MakeGenericInstanceType(Import.System.Exception));
                exceptionList = new VariableDefinition(genericType);
                exception     = new VariableDefinition(Import.System.Exception);

                method.Body.Variables.Add(exceptionList);
                method.Body.Variables.Add(exception);

                MethodReference listCtor = Import.System.Collections.List.Ctor;
                listCtor = listCtor.MakeGenericDeclaringType(Import.System.Exception);

                Instruction createListInstruction = Instruction.Create(OpCodes.Newobj, listCtor);
                processor.Append(createListInstruction);
                processor.Emit(OpCodes.Stloc, exceptionList);

                methodGenerator?.Append("List<Exception> list = new List<Exception>();", createListInstruction);
                methodGenerator?.Append(Environment.NewLine);
            }

            MethodReference funcCtor = Import.System.Func2Ctor;

            if (mapping != null)
            {
                int factoryIndex   = 0;
                var factoryMethods = new Dictionary <string, MethodDefinition>();

                foreach (Registration registration in mapping)
                {
                    try
                    {
                        Logger.Debug($"Processing map for {registration.TargetType.FullName}", DebugLogLevel.Verbose);

                        if (!factoryMethods.TryGetValue(registration.TargetType.FullName,
                                                        out MethodDefinition factoryMethod))
                        {
                            factoryMethod = GenerateFactoryMethod(registration.TargetType, factoryIndex, codeGenerator);
                            if (factoryMethod == null)
                            {
                                Logger.Debug($"No acceptable constructor for '{registration.TargetType.FullName}', skipping map",
                                             DebugLogLevel.Verbose);
                                continue;
                            }
                            factoryMethods[registration.TargetType.FullName] = factoryMethod;
                            factoryIndex++;
                            containerType.Methods.Add(factoryMethod);
                        }


                        var tryStart = Instruction.Create(OpCodes.Ldarg_0); //collection parameter
                        processor.Append(tryStart);

                        TypeReference importedKey = ModuleDefinition.ImportReference(registration.Key);
                        Logger.Debug(
                            $"Mapping {importedKey.FullName} => {registration.TargetType.FullName} ({registration.Lifetime})",
                            DebugLogLevel.Default);
                        processor.Emit(OpCodes.Ldtoken, importedKey);
                        processor.Emit(OpCodes.Call, Import.System.Type.GetTypeFromHandle);

                        processor.Emit(OpCodes.Ldtoken, ModuleDefinition.ImportReference(registration.TargetType));
                        processor.Emit(OpCodes.Call, Import.System.Type.GetTypeFromHandle);

                        processor.Emit(OpCodes.Ldnull);
                        processor.Emit(OpCodes.Ldftn, factoryMethod);
                        processor.Emit(OpCodes.Newobj,
                                       ModuleDefinition.ImportReference(
                                           funcCtor.MakeGenericDeclaringType(Import.System.IServiceProvider,
                                                                             ModuleDefinition.ImportReference(registration.TargetType))));

                        processor.Emit(OpCodes.Ldc_I4, (int)registration.Lifetime);

                        processor.Emit(OpCodes.Call, Import.AutoDI.ServiceCollectionMixins.AddAutoDIService);
                        processor.Emit(OpCodes.Pop);


                        if (settings.DebugExceptions)
                        {
                            Instruction afterCatch = Instruction.Create(OpCodes.Nop);
                            processor.Emit(OpCodes.Leave_S, afterCatch);

                            Instruction handlerStart = Instruction.Create(OpCodes.Stloc, exception);
                            processor.Append(handlerStart);
                            processor.Emit(OpCodes.Ldloc, exceptionList);
                            processor.Emit(OpCodes.Ldstr, $"Error adding type '{registration.TargetType.FullName}' with key '{registration.Key.FullName}'");
                            processor.Emit(OpCodes.Ldloc, exception);

                            processor.Emit(OpCodes.Newobj, Import.AutoDI.Exceptions.AutoDIExceptionCtor);
                            var listAdd = Import.System.Collections.List.Add;
                            listAdd = listAdd.MakeGenericDeclaringType(Import.System.Exception);

                            processor.Emit(OpCodes.Callvirt, listAdd);

                            Instruction handlerEnd = Instruction.Create(OpCodes.Leave_S, afterCatch);
                            processor.Append(handlerEnd);

                            var exceptionHandler =
                                new ExceptionHandler(ExceptionHandlerType.Catch)
                            {
                                CatchType    = Import.System.Exception,
                                TryStart     = tryStart,
                                TryEnd       = handlerStart,
                                HandlerStart = handlerStart,
                                HandlerEnd   = afterCatch
                            };

                            method.Body.ExceptionHandlers.Add(exceptionHandler);

                            processor.Append(afterCatch);
                            if (methodGenerator != null)
                            {
                                methodGenerator.Append("try" + Environment.NewLine + "{" + Environment.NewLine);
                                methodGenerator.Append($"    {serviceCollection.Name}.{Import.AutoDI.ServiceCollectionMixins.AddAutoDIService.Name}(typeof({importedKey.FullNameCSharp()}), typeof({registration.TargetType.FullNameCSharp()}), new Func<{Import.System.IServiceProvider.NameCSharp()}, {registration.TargetType.FullNameCSharp()}>({factoryMethod.Name}), Lifetime.{registration.Lifetime});", tryStart);
                                methodGenerator.Append(Environment.NewLine + "}" + Environment.NewLine + "catch(Exception innerException)" + Environment.NewLine + "{" + Environment.NewLine);
                                methodGenerator.Append($"    list.{listAdd.Name}(new {Import.AutoDI.Exceptions.AutoDIExceptionCtor.DeclaringType.Name}(\"Error adding type '{registration.TargetType.FullName}' with key '{registration.Key.FullName}'\", innerException));", handlerStart);
                                methodGenerator.Append(Environment.NewLine + "}" + Environment.NewLine);
                            }
                        }
                        else if (methodGenerator != null)
                        {
                            methodGenerator.Append($"{serviceCollection.Name}.{Import.AutoDI.ServiceCollectionMixins.AddAutoDIService.Name}(typeof({importedKey.FullNameCSharp()}), typeof({registration.TargetType.FullNameCSharp()}), new Func<{Import.System.IServiceProvider.NameCSharp()}, {registration.TargetType.FullNameCSharp()}>({factoryMethod.Name}), Lifetime.{registration.Lifetime});", tryStart);
                            methodGenerator.Append(Environment.NewLine);
                        }
                    }
                    catch (MultipleConstructorException e)
                    {
                        Logger.Error($"Failed to create map for {registration}\r\n{e}");
                    }
                    catch (Exception e)
                    {
                        Logger.Warning($"Failed to create map for {registration}\r\n{e}");
                    }
                }
            }

            Instruction @return = Instruction.Create(OpCodes.Ret);

            if (settings.DebugExceptions)
            {
                Instruction loadList = Instruction.Create(OpCodes.Ldloc, exceptionList);
                processor.Append(loadList);

                var listCount = Import.System.Collections.List.Count;
                listCount = listCount.MakeGenericDeclaringType(Import.System.Exception);
                processor.Emit(OpCodes.Callvirt, listCount);
                processor.Emit(OpCodes.Ldc_I4_0);
                processor.Emit(OpCodes.Cgt);
                processor.Emit(OpCodes.Brfalse_S, @return);

                Instruction ldStr = Instruction.Create(OpCodes.Ldstr, $"Error in {Constants.TypeName}.AddServices() generated method");
                processor.Append(ldStr);
                processor.Emit(OpCodes.Ldloc, exceptionList);

                processor.Emit(OpCodes.Newobj, Import.System.AggregateExceptionCtor);
                processor.Emit(OpCodes.Throw);

                if (methodGenerator != null)
                {
                    methodGenerator.Append("if (list.Count > 0)", loadList);
                    methodGenerator.Append(Environment.NewLine + "{" + Environment.NewLine);
                    methodGenerator.Append($"    throw new {Import.System.AggregateExceptionCtor.DeclaringType.Name}(\"Error in {Constants.TypeName}.{method.Name}() generated method\", list);", ldStr);
                    methodGenerator.Append(Environment.NewLine + "}" + Environment.NewLine);
                }
            }

            processor.Append(@return);

            method.Body.OptimizeMacros();

            return(method);
        }