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); }