From d6d5dc994028b8403e3e7749a3368d4218d017c6 Mon Sep 17 00:00:00 2001 From: Hariharan Subramanian Date: Thu, 17 Oct 2019 12:26:53 +0530 Subject: [PATCH 1/3] Added additional stacktrace information for Loggers. --- AutoDI.Build/AssemblyResolver.cs | 239 +++--- AutoDI.Build/ILogger.cs | 4 +- AutoDI.Build/ProcessAssemblyTask.Container.cs | 696 +++++++++--------- AutoDI.Build/ProcessAssemblyTask.Settings.cs | 3 +- AutoDI.Build/ProcessAssemblyTask.cs | 3 +- AutoDI.Build/StackTracer.cs | 35 + AutoDI.Build/TaskLogger.cs | 69 +- 7 files changed, 550 insertions(+), 499 deletions(-) create mode 100644 AutoDI.Build/StackTracer.cs diff --git a/AutoDI.Build/AssemblyResolver.cs b/AutoDI.Build/AssemblyResolver.cs index 3b4928f..c83c6e7 100644 --- a/AutoDI.Build/AssemblyResolver.cs +++ b/AutoDI.Build/AssemblyResolver.cs @@ -6,135 +6,136 @@ namespace AutoDI.Build { - internal class AssemblyResolver : BaseAssemblyResolver, ITypeResolver - { - private readonly ILogger _logger; - private readonly IDictionary _assemblyCache; - private readonly IDictionary _typeCache; + internal class AssemblyResolver : BaseAssemblyResolver, ITypeResolver + { + private readonly ILogger _logger; + private readonly IDictionary _assemblyCache; + private readonly IDictionary _typeCache; - public AssemblyResolver(IEnumerable assembliesToInclude, ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _assemblyCache = new Dictionary(StringComparer.Ordinal); - _typeCache = new Dictionary(StringComparer.Ordinal); - AddSearchDirectory(Path.GetFullPath(".")); - AddSearchDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); - ResolveFailure += OnResolveFailure; - foreach (var assemblyPath in assembliesToInclude) - { - AssemblyDefinition assembly = GetFromPath(assemblyPath); - if (assembly != null) - { - logger.Debug($"Caching ref '{assembly.Name.FullName}' from '{assembly.MainModule.FileName}'", DebugLogLevel.Verbose); - _assemblyCache[assembly.Name.Name] = assembly; - } - } - - logger.Info("Done loading referenced assemblies"); - } + public AssemblyResolver(IEnumerable assembliesToInclude, ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assemblyCache = new Dictionary(StringComparer.Ordinal); + _typeCache = new Dictionary(StringComparer.Ordinal); + AddSearchDirectory(Path.GetFullPath(".")); + AddSearchDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + ResolveFailure += OnResolveFailure; + foreach (var assemblyPath in assembliesToInclude) + { + AssemblyDefinition assembly = GetFromPath(assemblyPath); + if (assembly != null) + { + logger.Debug($"Caching ref '{assembly.Name.FullName}' from '{assembly.MainModule.FileName}'", DebugLogLevel.Verbose); + _assemblyCache[assembly.Name.Name] = assembly; + } + } - public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters readParameters) - { - if (name == null) throw new ArgumentNullException(nameof(name)); + logger.Info("Done loading referenced assemblies"); + } - if (_assemblyCache.TryGetValue(name.Name, out AssemblyDefinition assemblyDefinition)) - { - _logger.Debug($"Loaded assembly {name.FullName} from cache", DebugLogLevel.Verbose); - return assemblyDefinition; - } - assemblyDefinition = base.Resolve(name, readParameters); - - if (assemblyDefinition != null) - { - _logger.Debug($"Resolved assembly {name.FullName} from '{assemblyDefinition.MainModule.FileName}'", DebugLogLevel.Verbose); - _assemblyCache[name.Name] = assemblyDefinition; - } - else - { - _logger.Info($"Could not find {name.FullName}"); - } - return assemblyDefinition; - } + public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters readParameters) + { + if (name == null) throw new ArgumentNullException(nameof(name)); - public TypeDefinition ResolveType(string fullTypeName) - { - if (fullTypeName == null) throw new ArgumentNullException(nameof(fullTypeName)); - if (_typeCache.TryGetValue(fullTypeName, out TypeDefinition type)) - { - return type; - } + if (_assemblyCache.TryGetValue(name.Name, out AssemblyDefinition assemblyDefinition)) + { + _logger.Debug($"Loaded assembly {name.FullName} from cache", DebugLogLevel.Verbose); + return assemblyDefinition; + } + assemblyDefinition = base.Resolve(name, readParameters); - foreach (AssemblyDefinition assembly in _assemblyCache.Values) - { - type = assembly.MainModule.GetType(fullTypeName); - if (type != null) - { - return _typeCache[fullTypeName] = type; - } - } - return null; - } + if (assemblyDefinition != null) + { + _logger.Debug($"Resolved assembly {name.FullName} from '{assemblyDefinition.MainModule.FileName}'", DebugLogLevel.Verbose); + _assemblyCache[name.Name] = assemblyDefinition; + } + else + { + _logger.Info($"Could not find {name.FullName}"); + } + return assemblyDefinition; + } - protected override void Dispose(bool disposing) - { - foreach (AssemblyDefinition assemblyDefinition in _assemblyCache.Values) - { - assemblyDefinition?.Dispose(); - } - _assemblyCache.Clear(); - base.Dispose(disposing); - } + public TypeDefinition ResolveType(string fullTypeName) + { + if (fullTypeName == null) throw new ArgumentNullException(nameof(fullTypeName)); + if (_typeCache.TryGetValue(fullTypeName, out TypeDefinition type)) + { + return type; + } - private AssemblyDefinition OnResolveFailure(object sender, AssemblyNameReference reference) - { - Assembly assembly; - try - { - if (_assemblyCache.TryGetValue(reference.Name, out AssemblyDefinition cached)) - { - return cached; - } + foreach (AssemblyDefinition assembly in _assemblyCache.Values) + { + type = assembly.MainModule.GetType(fullTypeName); + if (type != null) + { + return _typeCache[fullTypeName] = type; + } + } + return null; + } + + protected override void Dispose(bool disposing) + { + foreach (AssemblyDefinition assemblyDefinition in _assemblyCache.Values) + { + assemblyDefinition?.Dispose(); + } + _assemblyCache.Clear(); + base.Dispose(disposing); + } + + private AssemblyDefinition OnResolveFailure(object sender, AssemblyNameReference reference) + { + Assembly assembly; + try + { + if (_assemblyCache.TryGetValue(reference.Name, out AssemblyDefinition cached)) + { + return cached; + } #pragma warning disable 618 - assembly = Assembly.LoadWithPartialName(reference.Name); + assembly = Assembly.LoadWithPartialName(reference.Name); #pragma warning restore 618 - } - catch (FileNotFoundException) - { - _logger.Warning($"Failed to resolve '{reference.Name}'"); - assembly = null; - } + } + catch (FileNotFoundException ex) + { + var additionalInformation = StackTracer.GetStackTrace(ex); + _logger.Warning($"Failed to resolve '{reference.Name}'",additionalInformation); + assembly = null; + } - if (!string.IsNullOrWhiteSpace(assembly?.CodeBase)) - { - string path = new Uri(assembly.CodeBase).AbsolutePath; - return GetFromPath(path); - } + if (!string.IsNullOrWhiteSpace(assembly?.CodeBase)) + { + string path = new Uri(assembly.CodeBase).AbsolutePath; + return GetFromPath(path); + } - return null; - } + return null; + } - private AssemblyDefinition GetFromPath(string filePath) - { - if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) - { - if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(Uri.UnescapeDataString(filePath))) - { - filePath = Uri.UnescapeDataString(filePath); - } - else - { - _logger.Warning($"Could not find assembly '{filePath}'"); - return null; - } - } - var readerParameters = new ReaderParameters(ReadingMode.Deferred) - { - ReadWrite = false, - ReadSymbols = false, - AssemblyResolver = this - }; - _logger.Debug($"Loading '{filePath}'", DebugLogLevel.Verbose); - return AssemblyDefinition.ReadAssembly(filePath, readerParameters); - } - } + private AssemblyDefinition GetFromPath(string filePath) + { + if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) + { + if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(Uri.UnescapeDataString(filePath))) + { + filePath = Uri.UnescapeDataString(filePath); + } + else + { + _logger.Warning($"Could not find assembly '{filePath}'"); + return null; + } + } + var readerParameters = new ReaderParameters(ReadingMode.Deferred) + { + ReadWrite = false, + ReadSymbols = false, + AssemblyResolver = this + }; + _logger.Debug($"Loading '{filePath}'", DebugLogLevel.Verbose); + return AssemblyDefinition.ReadAssembly(filePath, readerParameters); + } + } } \ No newline at end of file diff --git a/AutoDI.Build/ILogger.cs b/AutoDI.Build/ILogger.cs index d50491e..9037c12 100644 --- a/AutoDI.Build/ILogger.cs +++ b/AutoDI.Build/ILogger.cs @@ -7,7 +7,7 @@ public interface ILogger void Debug(string message, DebugLogLevel debugLevel); void Info(string message); - void Warning(string message); - void Error(string message); + void Warning(string message,AdditionalInformation additionalInformation=null); + void Error(string message,AdditionalInformation additionalInformation); } } \ No newline at end of file diff --git a/AutoDI.Build/ProcessAssemblyTask.Container.cs b/AutoDI.Build/ProcessAssemblyTask.Container.cs index 8e3e30d..788096c 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Container.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Container.cs @@ -8,354 +8,356 @@ namespace AutoDI.Build { - partial class ProcessAssemblyTask - { - //TODO: out parameters... yuck - private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, ICodeGenerator codeGenerator, - out MethodDefinition initMethod) - { - var containerType = new TypeDefinition(Constants.Namespace, Constants.TypeName, - TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed - | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) - { - BaseType = Import.System.Object - }; - - FieldDefinition globalServiceProvider = - ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); - containerType.Fields.Add(globalServiceProvider); - - MethodDefinition configureMethod = GenerateAddServicesMethod(mapping, settings, containerType, codeGenerator); - containerType.Methods.Add(configureMethod); - - initMethod = GenerateInitMethod(configureMethod, globalServiceProvider); - containerType.Methods.Add(initMethod); - - MethodDefinition disposeMethod = GenerateDisposeMethod(globalServiceProvider); - containerType.Methods.Add(disposeMethod); - - return containerType; - } - - 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 list = new List();", createListInstruction); - methodGenerator?.Append(Environment.NewLine); - } - - MethodReference funcCtor = Import.System.Func2Ctor; - - if (mapping != null) - { - int factoryIndex = 0; - var factoryMethods = new Dictionary(); - - 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; - } - - private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int index, ICodeGenerator codeGenerator) - { - if (!targetType.CanMapType()) return null; - - MethodDefinition targetTypeCtor = targetType.GetMappingConstructor(); - if (targetTypeCtor == null) return null; - - var factory = new MethodDefinition($"<{targetType.Name}>_generated_{index}", - MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, - ModuleDefinition.ImportReference(targetType)); - factory.Parameters.Add(new ParameterDefinition("serviceProvider", ParameterAttributes.None, Import.System.IServiceProvider)); - - ILProcessor factoryProcessor = factory.Body.GetILProcessor(); - - MethodReference getServiceMethod = Import.DependencyInjection.ServiceProviderServiceExtensionsGetService; - - foreach (ParameterDefinition parameter in targetTypeCtor.Parameters) - { - factoryProcessor.Emit(OpCodes.Ldarg_0); - var genericGetService = new GenericInstanceMethod(getServiceMethod); - genericGetService.GenericArguments.Add(ModuleDefinition.ImportReference(parameter.ParameterType)); - factoryProcessor.Emit(OpCodes.Call, genericGetService); - } - - factoryProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(targetTypeCtor)); - factoryProcessor.Emit(OpCodes.Ret); - - IMethodGenerator methodGenerator = codeGenerator?.Method(factory); - if (methodGenerator != null) - { - var parameters = string.Join(", ", targetTypeCtor.Parameters.Select(x => $"serviceProvider.GetService<{x.ParameterType.NameCSharp(true)}>()")); - methodGenerator.Append($"return new {targetType.NameCSharp(true)}({parameters});", factoryProcessor.Body.Instructions.First()); - methodGenerator.Append(Environment.NewLine); - } - return factory; - } - - private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, FieldDefinition globalServiceProvider) - { - var initMethod = new MethodDefinition(Constants.InitMethodName, - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); - var configureAction = new ParameterDefinition("configure", ParameterAttributes.None, Import.System.Action.Type.MakeGenericInstanceType(Import.AutoDI.IApplicationBuilder.Type)); - initMethod.Parameters.Add(configureAction); - - var applicationBuilder = new VariableDefinition(Import.AutoDI.IApplicationBuilder.Type); - initMethod.Body.Variables.Add(applicationBuilder); - ILProcessor initProcessor = initMethod.Body.GetILProcessor(); - - Instruction createApplicationbuilder = Instruction.Create(OpCodes.Newobj, Import.AutoDI.ApplicationBuilder.Ctor); - - initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Brfalse_S, createApplicationbuilder); - //Compare - initProcessor.Emit(OpCodes.Newobj, Import.AutoDI.Exceptions.AlreadyInitializedExceptionCtor); - initProcessor.Emit(OpCodes.Throw); - - initProcessor.Append(createApplicationbuilder); - initProcessor.Emit(OpCodes.Stloc_0); - - initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder - initProcessor.Emit(OpCodes.Ldnull); - initProcessor.Emit(OpCodes.Ldftn, configureMethod); - initProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(Import.System.Action.Ctor.MakeGenericDeclaringType(Import.DependencyInjection.IServiceCollection))); - initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.ConfigureServices); - initProcessor.Emit(OpCodes.Pop); - - MethodDefinition setupMethod = SetupMethod.Find(ModuleDefinition, Logger); - if (setupMethod != null) - { - Logger.Debug($"Found setup method '{setupMethod.FullName}'", DebugLogLevel.Default); - initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder - initProcessor.Emit(OpCodes.Call, setupMethod); - initProcessor.Emit(OpCodes.Nop); - } - else - { - Logger.Debug("No setup method found", DebugLogLevel.Default); - } - - Instruction loadForBuild = Instruction.Create(OpCodes.Ldloc_0); - - initProcessor.Emit(OpCodes.Ldarg_0); - initProcessor.Emit(OpCodes.Brfalse_S, loadForBuild); - initProcessor.Emit(OpCodes.Ldarg_0); - initProcessor.Emit(OpCodes.Ldloc_0); - initProcessor.Emit(OpCodes.Callvirt, ModuleDefinition.ImportReference(Import.System.Action.Invoke.MakeGenericDeclaringType(Import.AutoDI.IApplicationBuilder.Type))); - - - initProcessor.Append(loadForBuild); - initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.Build); - initProcessor.Emit(OpCodes.Stsfld, globalServiceProvider); + partial class ProcessAssemblyTask + { + //TODO: out parameters... yuck + private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, ICodeGenerator codeGenerator, + out MethodDefinition initMethod) + { + var containerType = new TypeDefinition(Constants.Namespace, Constants.TypeName, + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed + | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) + { + BaseType = Import.System.Object + }; + + FieldDefinition globalServiceProvider = + ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); + containerType.Fields.Add(globalServiceProvider); + + MethodDefinition configureMethod = GenerateAddServicesMethod(mapping, settings, containerType, codeGenerator); + containerType.Methods.Add(configureMethod); + + initMethod = GenerateInitMethod(configureMethod, globalServiceProvider); + containerType.Methods.Add(initMethod); + + MethodDefinition disposeMethod = GenerateDisposeMethod(globalServiceProvider); + containerType.Methods.Add(disposeMethod); + + return containerType; + } + + 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 list = new List();", createListInstruction); + methodGenerator?.Append(Environment.NewLine); + } + + MethodReference funcCtor = Import.System.Func2Ctor; + + if (mapping != null) + { + int factoryIndex = 0; + var factoryMethods = new Dictionary(); + + 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) + { + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Error($"Failed to create map for {registration}\r\n{e}", additionalInformation); + } + catch (Exception e) + { + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Warning($"Failed to create map for {registration}\r\n{e}", additionalInformation); + } + } + } + + 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; + } + + private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int index, ICodeGenerator codeGenerator) + { + if (!targetType.CanMapType()) return null; + + MethodDefinition targetTypeCtor = targetType.GetMappingConstructor(); + if (targetTypeCtor == null) return null; + + var factory = new MethodDefinition($"<{targetType.Name}>_generated_{index}", + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + ModuleDefinition.ImportReference(targetType)); + factory.Parameters.Add(new ParameterDefinition("serviceProvider", ParameterAttributes.None, Import.System.IServiceProvider)); + + ILProcessor factoryProcessor = factory.Body.GetILProcessor(); + + MethodReference getServiceMethod = Import.DependencyInjection.ServiceProviderServiceExtensionsGetService; + + foreach (ParameterDefinition parameter in targetTypeCtor.Parameters) + { + factoryProcessor.Emit(OpCodes.Ldarg_0); + var genericGetService = new GenericInstanceMethod(getServiceMethod); + genericGetService.GenericArguments.Add(ModuleDefinition.ImportReference(parameter.ParameterType)); + factoryProcessor.Emit(OpCodes.Call, genericGetService); + } + + factoryProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(targetTypeCtor)); + factoryProcessor.Emit(OpCodes.Ret); + + IMethodGenerator methodGenerator = codeGenerator?.Method(factory); + if (methodGenerator != null) + { + var parameters = string.Join(", ", targetTypeCtor.Parameters.Select(x => $"serviceProvider.GetService<{x.ParameterType.NameCSharp(true)}>()")); + methodGenerator.Append($"return new {targetType.NameCSharp(true)}({parameters});", factoryProcessor.Body.Instructions.First()); + methodGenerator.Append(Environment.NewLine); + } + return factory; + } + + private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, FieldDefinition globalServiceProvider) + { + var initMethod = new MethodDefinition(Constants.InitMethodName, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); + var configureAction = new ParameterDefinition("configure", ParameterAttributes.None, Import.System.Action.Type.MakeGenericInstanceType(Import.AutoDI.IApplicationBuilder.Type)); + initMethod.Parameters.Add(configureAction); + + var applicationBuilder = new VariableDefinition(Import.AutoDI.IApplicationBuilder.Type); + initMethod.Body.Variables.Add(applicationBuilder); + ILProcessor initProcessor = initMethod.Body.GetILProcessor(); + + Instruction createApplicationbuilder = Instruction.Create(OpCodes.Newobj, Import.AutoDI.ApplicationBuilder.Ctor); + + initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); + initProcessor.Emit(OpCodes.Brfalse_S, createApplicationbuilder); + //Compare + initProcessor.Emit(OpCodes.Newobj, Import.AutoDI.Exceptions.AlreadyInitializedExceptionCtor); + initProcessor.Emit(OpCodes.Throw); + + initProcessor.Append(createApplicationbuilder); + initProcessor.Emit(OpCodes.Stloc_0); + + initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder + initProcessor.Emit(OpCodes.Ldnull); + initProcessor.Emit(OpCodes.Ldftn, configureMethod); + initProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(Import.System.Action.Ctor.MakeGenericDeclaringType(Import.DependencyInjection.IServiceCollection))); + initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.ConfigureServices); + initProcessor.Emit(OpCodes.Pop); + + MethodDefinition setupMethod = SetupMethod.Find(ModuleDefinition, Logger); + if (setupMethod != null) + { + Logger.Debug($"Found setup method '{setupMethod.FullName}'", DebugLogLevel.Default); + initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder + initProcessor.Emit(OpCodes.Call, setupMethod); + initProcessor.Emit(OpCodes.Nop); + } + else + { + Logger.Debug("No setup method found", DebugLogLevel.Default); + } + + Instruction loadForBuild = Instruction.Create(OpCodes.Ldloc_0); + + initProcessor.Emit(OpCodes.Ldarg_0); + initProcessor.Emit(OpCodes.Brfalse_S, loadForBuild); + initProcessor.Emit(OpCodes.Ldarg_0); + initProcessor.Emit(OpCodes.Ldloc_0); + initProcessor.Emit(OpCodes.Callvirt, ModuleDefinition.ImportReference(Import.System.Action.Invoke.MakeGenericDeclaringType(Import.AutoDI.IApplicationBuilder.Type))); + + + initProcessor.Append(loadForBuild); + initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.Build); + initProcessor.Emit(OpCodes.Stsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Register); - - - initProcessor.Emit(OpCodes.Ret); - - return initMethod; - } - - private MethodDefinition GenerateDisposeMethod(FieldDefinition globalServiceProvider) - { - var disposeMethod = new MethodDefinition("Dispose", - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); - - VariableDefinition disposable = new VariableDefinition(Import.System.IDisposable.Type); - disposeMethod.Body.Variables.Add(disposable); - - ILProcessor processor = disposeMethod.Body.GetILProcessor(); - Instruction afterDispose = Instruction.Create(OpCodes.Nop); - - processor.Emit(OpCodes.Ldsfld, globalServiceProvider); - processor.Emit(OpCodes.Isinst, Import.System.IDisposable.Type); - processor.Emit(OpCodes.Dup); - processor.Emit(OpCodes.Stloc_0); //disposable - processor.Emit(OpCodes.Brfalse_S, afterDispose); - processor.Emit(OpCodes.Ldloc_0); //disposable - processor.Emit(OpCodes.Callvirt, Import.System.IDisposable.Dispose); - - processor.Append(afterDispose); - - processor.Emit(OpCodes.Ldsfld, globalServiceProvider); - processor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Unregister); - processor.Emit(OpCodes.Pop); - - processor.Emit(OpCodes.Ldnull); - processor.Emit(OpCodes.Stsfld, globalServiceProvider); - - processor.Emit(OpCodes.Ret); - return disposeMethod; - } + initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); + initProcessor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Register); + + + initProcessor.Emit(OpCodes.Ret); + + return initMethod; + } + + private MethodDefinition GenerateDisposeMethod(FieldDefinition globalServiceProvider) + { + var disposeMethod = new MethodDefinition("Dispose", + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); + + VariableDefinition disposable = new VariableDefinition(Import.System.IDisposable.Type); + disposeMethod.Body.Variables.Add(disposable); + + ILProcessor processor = disposeMethod.Body.GetILProcessor(); + Instruction afterDispose = Instruction.Create(OpCodes.Nop); + + processor.Emit(OpCodes.Ldsfld, globalServiceProvider); + processor.Emit(OpCodes.Isinst, Import.System.IDisposable.Type); + processor.Emit(OpCodes.Dup); + processor.Emit(OpCodes.Stloc_0); //disposable + processor.Emit(OpCodes.Brfalse_S, afterDispose); + processor.Emit(OpCodes.Ldloc_0); //disposable + processor.Emit(OpCodes.Callvirt, Import.System.IDisposable.Dispose); + + processor.Append(afterDispose); + + processor.Emit(OpCodes.Ldsfld, globalServiceProvider); + processor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Unregister); + processor.Emit(OpCodes.Pop); + + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Stsfld, globalServiceProvider); + + processor.Emit(OpCodes.Ret); + return disposeMethod; + } - } + } } \ No newline at end of file diff --git a/AutoDI.Build/ProcessAssemblyTask.Settings.cs b/AutoDI.Build/ProcessAssemblyTask.Settings.cs index 1ea93b1..91eff6e 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Settings.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Settings.cs @@ -13,7 +13,8 @@ private Settings LoadSettings() } catch (SettingsParseException e) { - Logger.Error($"Failed to parse AutoDI settings{Environment.NewLine}{e.Message}"); + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Error($"Failed to parse AutoDI settings{Environment.NewLine}{e.Message}",additionalInformation); return null; } diff --git a/AutoDI.Build/ProcessAssemblyTask.cs b/AutoDI.Build/ProcessAssemblyTask.cs index 2277bb0..7a9996b 100644 --- a/AutoDI.Build/ProcessAssemblyTask.cs +++ b/AutoDI.Build/ProcessAssemblyTask.cs @@ -94,7 +94,8 @@ protected override bool WeaveAssembly() var sb = new StringBuilder(); for (Exception e = ex; e != null; e = e.InnerException) sb.AppendLine(e.ToString()); - Logger.Error(sb.ToString()); + var additionalInformation = StackTracer.GetStackTrace(ex); + Logger.Error(sb.ToString(),additionalInformation); return false; } } diff --git a/AutoDI.Build/StackTracer.cs b/AutoDI.Build/StackTracer.cs new file mode 100644 index 0000000..ee5941b --- /dev/null +++ b/AutoDI.Build/StackTracer.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics; + +namespace AutoDI.Build +{ + public static class StackTracer + { + public static AdditionalInformation GetStackTrace(Exception exception) + { + var trace = new StackTrace(exception, true); + var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; + var additionalInformation = new AdditionalInformation(); + if (reflectedType != null) + { + additionalInformation = new AdditionalInformation() + { + Column = trace.GetFrame(0).GetFileColumnNumber(), + Line = trace.GetFrame(0).GetFileLineNumber(), + MethodName = reflectedType.FullName, + File = trace.GetFrame(0).GetFileName() + }; + + } + return additionalInformation; + } + } + + public class AdditionalInformation + { + public string MethodName { get; set; } + public string File { get; set; } + public int Line { get; set; } + public int Column { get; set; } + } +} diff --git a/AutoDI.Build/TaskLogger.cs b/AutoDI.Build/TaskLogger.cs index b094455..612c4ca 100644 --- a/AutoDI.Build/TaskLogger.cs +++ b/AutoDI.Build/TaskLogger.cs @@ -4,43 +4,54 @@ namespace AutoDI.Build { - internal class TaskLogger : ILogger - { - private readonly Task _task; + internal class TaskLogger : ILogger + { + private readonly Task _task; - public bool ErrorLogged { get; private set; } + public bool ErrorLogged { get; private set; } - public DebugLogLevel DebugLogLevel { get; set; } + public DebugLogLevel DebugLogLevel { get; set; } - private const string MessageSender = "AutoDI:"; + private const string MessageSender = "AutoDI:"; - public TaskLogger(Task task) - { - _task = task ?? throw new ArgumentNullException(nameof(task)); - } + public TaskLogger(Task task) + { + _task = task ?? throw new ArgumentNullException(nameof(task)); + } - public void Error(string message) - { - ErrorLogged = true; - _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", null, 0, 0, 0, 0, $"{MessageSender} {message}", "", MessageSender)); - } + public void Error(string message, AdditionalInformation additionalInformation) + { + ErrorLogged = true; + _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + } - public void Debug(string message, DebugLogLevel debugLevel) - { - if (debugLevel >= DebugLogLevel) - { - _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Low)); - } - } + public void Debug(string message, DebugLogLevel debugLevel) + { + if (debugLevel >= DebugLogLevel) + { + _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Low)); + } + } - public void Info(string message) - { - _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Normal)); - } + public void Info(string message) + { + _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Normal)); + } - public void Warning(string message) + public void Warning(string message, AdditionalInformation additionalInformation) { - _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", null, 0, 0, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + if (additionalInformation == null) + { + _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", null, 0, 0, 0, 0, + $"{MessageSender} {message}", "", + MessageSender)); + } + else + { + _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", additionalInformation.File, + additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", + MessageSender)); + } } - } + } } \ No newline at end of file From b0d63be496d38a193bff8d3b46081b6182a946e6 Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Thu, 24 Oct 2019 22:32:05 -0700 Subject: [PATCH 2/3] White space cleanup --- AutoDI.Build/AssemblyResolver.cs | 234 +++--- AutoDI.Build/ProcessAssemblyTask.Container.cs | 698 +++++++++--------- AutoDI.Build/StackTracer.cs | 54 +- AutoDI.Build/TaskLogger.cs | 56 +- 4 files changed, 521 insertions(+), 521 deletions(-) diff --git a/AutoDI.Build/AssemblyResolver.cs b/AutoDI.Build/AssemblyResolver.cs index c83c6e7..72bd7d3 100644 --- a/AutoDI.Build/AssemblyResolver.cs +++ b/AutoDI.Build/AssemblyResolver.cs @@ -6,136 +6,136 @@ namespace AutoDI.Build { - internal class AssemblyResolver : BaseAssemblyResolver, ITypeResolver - { - private readonly ILogger _logger; - private readonly IDictionary _assemblyCache; - private readonly IDictionary _typeCache; + internal class AssemblyResolver : BaseAssemblyResolver, ITypeResolver + { + private readonly ILogger _logger; + private readonly IDictionary _assemblyCache; + private readonly IDictionary _typeCache; - public AssemblyResolver(IEnumerable assembliesToInclude, ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _assemblyCache = new Dictionary(StringComparer.Ordinal); - _typeCache = new Dictionary(StringComparer.Ordinal); - AddSearchDirectory(Path.GetFullPath(".")); - AddSearchDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); - ResolveFailure += OnResolveFailure; - foreach (var assemblyPath in assembliesToInclude) - { - AssemblyDefinition assembly = GetFromPath(assemblyPath); - if (assembly != null) - { - logger.Debug($"Caching ref '{assembly.Name.FullName}' from '{assembly.MainModule.FileName}'", DebugLogLevel.Verbose); - _assemblyCache[assembly.Name.Name] = assembly; - } - } + public AssemblyResolver(IEnumerable assembliesToInclude, ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assemblyCache = new Dictionary(StringComparer.Ordinal); + _typeCache = new Dictionary(StringComparer.Ordinal); + AddSearchDirectory(Path.GetFullPath(".")); + AddSearchDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + ResolveFailure += OnResolveFailure; + foreach (var assemblyPath in assembliesToInclude) + { + AssemblyDefinition assembly = GetFromPath(assemblyPath); + if (assembly != null) + { + logger.Debug($"Caching ref '{assembly.Name.FullName}' from '{assembly.MainModule.FileName}'", DebugLogLevel.Verbose); + _assemblyCache[assembly.Name.Name] = assembly; + } + } - logger.Info("Done loading referenced assemblies"); - } + logger.Info("Done loading referenced assemblies"); + } - public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters readParameters) - { - if (name == null) throw new ArgumentNullException(nameof(name)); + public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters readParameters) + { + if (name == null) throw new ArgumentNullException(nameof(name)); - if (_assemblyCache.TryGetValue(name.Name, out AssemblyDefinition assemblyDefinition)) - { - _logger.Debug($"Loaded assembly {name.FullName} from cache", DebugLogLevel.Verbose); - return assemblyDefinition; - } - assemblyDefinition = base.Resolve(name, readParameters); + if (_assemblyCache.TryGetValue(name.Name, out AssemblyDefinition assemblyDefinition)) + { + _logger.Debug($"Loaded assembly {name.FullName} from cache", DebugLogLevel.Verbose); + return assemblyDefinition; + } + assemblyDefinition = base.Resolve(name, readParameters); - if (assemblyDefinition != null) - { - _logger.Debug($"Resolved assembly {name.FullName} from '{assemblyDefinition.MainModule.FileName}'", DebugLogLevel.Verbose); - _assemblyCache[name.Name] = assemblyDefinition; - } - else - { - _logger.Info($"Could not find {name.FullName}"); - } - return assemblyDefinition; - } + if (assemblyDefinition != null) + { + _logger.Debug($"Resolved assembly {name.FullName} from '{assemblyDefinition.MainModule.FileName}'", DebugLogLevel.Verbose); + _assemblyCache[name.Name] = assemblyDefinition; + } + else + { + _logger.Info($"Could not find {name.FullName}"); + } + return assemblyDefinition; + } - public TypeDefinition ResolveType(string fullTypeName) - { - if (fullTypeName == null) throw new ArgumentNullException(nameof(fullTypeName)); - if (_typeCache.TryGetValue(fullTypeName, out TypeDefinition type)) - { - return type; - } + public TypeDefinition ResolveType(string fullTypeName) + { + if (fullTypeName == null) throw new ArgumentNullException(nameof(fullTypeName)); + if (_typeCache.TryGetValue(fullTypeName, out TypeDefinition type)) + { + return type; + } - foreach (AssemblyDefinition assembly in _assemblyCache.Values) - { - type = assembly.MainModule.GetType(fullTypeName); - if (type != null) - { - return _typeCache[fullTypeName] = type; - } - } - return null; - } + foreach (AssemblyDefinition assembly in _assemblyCache.Values) + { + type = assembly.MainModule.GetType(fullTypeName); + if (type != null) + { + return _typeCache[fullTypeName] = type; + } + } + return null; + } - protected override void Dispose(bool disposing) - { - foreach (AssemblyDefinition assemblyDefinition in _assemblyCache.Values) - { - assemblyDefinition?.Dispose(); - } - _assemblyCache.Clear(); - base.Dispose(disposing); - } + protected override void Dispose(bool disposing) + { + foreach (AssemblyDefinition assemblyDefinition in _assemblyCache.Values) + { + assemblyDefinition?.Dispose(); + } + _assemblyCache.Clear(); + base.Dispose(disposing); + } - private AssemblyDefinition OnResolveFailure(object sender, AssemblyNameReference reference) - { - Assembly assembly; - try - { - if (_assemblyCache.TryGetValue(reference.Name, out AssemblyDefinition cached)) - { - return cached; - } + private AssemblyDefinition OnResolveFailure(object sender, AssemblyNameReference reference) + { + Assembly assembly; + try + { + if (_assemblyCache.TryGetValue(reference.Name, out AssemblyDefinition cached)) + { + return cached; + } #pragma warning disable 618 - assembly = Assembly.LoadWithPartialName(reference.Name); + assembly = Assembly.LoadWithPartialName(reference.Name); #pragma warning restore 618 - } - catch (FileNotFoundException ex) - { + } + catch (FileNotFoundException ex) + { var additionalInformation = StackTracer.GetStackTrace(ex); - _logger.Warning($"Failed to resolve '{reference.Name}'",additionalInformation); - assembly = null; - } + _logger.Warning($"Failed to resolve '{reference.Name}'", additionalInformation); + assembly = null; + } - if (!string.IsNullOrWhiteSpace(assembly?.CodeBase)) - { - string path = new Uri(assembly.CodeBase).AbsolutePath; - return GetFromPath(path); - } + if (!string.IsNullOrWhiteSpace(assembly?.CodeBase)) + { + string path = new Uri(assembly.CodeBase).AbsolutePath; + return GetFromPath(path); + } - return null; - } + return null; + } - private AssemblyDefinition GetFromPath(string filePath) - { - if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) - { - if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(Uri.UnescapeDataString(filePath))) - { - filePath = Uri.UnescapeDataString(filePath); - } - else - { - _logger.Warning($"Could not find assembly '{filePath}'"); - return null; - } - } - var readerParameters = new ReaderParameters(ReadingMode.Deferred) - { - ReadWrite = false, - ReadSymbols = false, - AssemblyResolver = this - }; - _logger.Debug($"Loading '{filePath}'", DebugLogLevel.Verbose); - return AssemblyDefinition.ReadAssembly(filePath, readerParameters); - } - } + private AssemblyDefinition GetFromPath(string filePath) + { + if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) + { + if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(Uri.UnescapeDataString(filePath))) + { + filePath = Uri.UnescapeDataString(filePath); + } + else + { + _logger.Warning($"Could not find assembly '{filePath}'"); + return null; + } + } + var readerParameters = new ReaderParameters(ReadingMode.Deferred) + { + ReadWrite = false, + ReadSymbols = false, + AssemblyResolver = this + }; + _logger.Debug($"Loading '{filePath}'", DebugLogLevel.Verbose); + return AssemblyDefinition.ReadAssembly(filePath, readerParameters); + } + } } \ No newline at end of file diff --git a/AutoDI.Build/ProcessAssemblyTask.Container.cs b/AutoDI.Build/ProcessAssemblyTask.Container.cs index 788096c..4331e25 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Container.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Container.cs @@ -8,356 +8,356 @@ namespace AutoDI.Build { - partial class ProcessAssemblyTask - { - //TODO: out parameters... yuck - private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, ICodeGenerator codeGenerator, - out MethodDefinition initMethod) - { - var containerType = new TypeDefinition(Constants.Namespace, Constants.TypeName, - TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed - | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) - { - BaseType = Import.System.Object - }; - - FieldDefinition globalServiceProvider = - ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); - containerType.Fields.Add(globalServiceProvider); - - MethodDefinition configureMethod = GenerateAddServicesMethod(mapping, settings, containerType, codeGenerator); - containerType.Methods.Add(configureMethod); - - initMethod = GenerateInitMethod(configureMethod, globalServiceProvider); - containerType.Methods.Add(initMethod); - - MethodDefinition disposeMethod = GenerateDisposeMethod(globalServiceProvider); - containerType.Methods.Add(disposeMethod); - - return containerType; - } - - 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 list = new List();", createListInstruction); - methodGenerator?.Append(Environment.NewLine); - } - - MethodReference funcCtor = Import.System.Func2Ctor; - - if (mapping != null) - { - int factoryIndex = 0; - var factoryMethods = new Dictionary(); - - 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) - { - var additionalInformation = StackTracer.GetStackTrace(e); - Logger.Error($"Failed to create map for {registration}\r\n{e}", additionalInformation); - } - catch (Exception e) - { - var additionalInformation = StackTracer.GetStackTrace(e); - Logger.Warning($"Failed to create map for {registration}\r\n{e}", additionalInformation); - } - } - } - - 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; - } - - private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int index, ICodeGenerator codeGenerator) - { - if (!targetType.CanMapType()) return null; - - MethodDefinition targetTypeCtor = targetType.GetMappingConstructor(); - if (targetTypeCtor == null) return null; - - var factory = new MethodDefinition($"<{targetType.Name}>_generated_{index}", - MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, - ModuleDefinition.ImportReference(targetType)); - factory.Parameters.Add(new ParameterDefinition("serviceProvider", ParameterAttributes.None, Import.System.IServiceProvider)); - - ILProcessor factoryProcessor = factory.Body.GetILProcessor(); - - MethodReference getServiceMethod = Import.DependencyInjection.ServiceProviderServiceExtensionsGetService; - - foreach (ParameterDefinition parameter in targetTypeCtor.Parameters) - { - factoryProcessor.Emit(OpCodes.Ldarg_0); - var genericGetService = new GenericInstanceMethod(getServiceMethod); - genericGetService.GenericArguments.Add(ModuleDefinition.ImportReference(parameter.ParameterType)); - factoryProcessor.Emit(OpCodes.Call, genericGetService); - } - - factoryProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(targetTypeCtor)); - factoryProcessor.Emit(OpCodes.Ret); - - IMethodGenerator methodGenerator = codeGenerator?.Method(factory); - if (methodGenerator != null) - { - var parameters = string.Join(", ", targetTypeCtor.Parameters.Select(x => $"serviceProvider.GetService<{x.ParameterType.NameCSharp(true)}>()")); - methodGenerator.Append($"return new {targetType.NameCSharp(true)}({parameters});", factoryProcessor.Body.Instructions.First()); - methodGenerator.Append(Environment.NewLine); - } - return factory; - } - - private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, FieldDefinition globalServiceProvider) - { - var initMethod = new MethodDefinition(Constants.InitMethodName, - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); - var configureAction = new ParameterDefinition("configure", ParameterAttributes.None, Import.System.Action.Type.MakeGenericInstanceType(Import.AutoDI.IApplicationBuilder.Type)); - initMethod.Parameters.Add(configureAction); - - var applicationBuilder = new VariableDefinition(Import.AutoDI.IApplicationBuilder.Type); - initMethod.Body.Variables.Add(applicationBuilder); - ILProcessor initProcessor = initMethod.Body.GetILProcessor(); - - Instruction createApplicationbuilder = Instruction.Create(OpCodes.Newobj, Import.AutoDI.ApplicationBuilder.Ctor); - - initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Brfalse_S, createApplicationbuilder); - //Compare - initProcessor.Emit(OpCodes.Newobj, Import.AutoDI.Exceptions.AlreadyInitializedExceptionCtor); - initProcessor.Emit(OpCodes.Throw); - - initProcessor.Append(createApplicationbuilder); - initProcessor.Emit(OpCodes.Stloc_0); - - initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder - initProcessor.Emit(OpCodes.Ldnull); - initProcessor.Emit(OpCodes.Ldftn, configureMethod); - initProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(Import.System.Action.Ctor.MakeGenericDeclaringType(Import.DependencyInjection.IServiceCollection))); - initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.ConfigureServices); - initProcessor.Emit(OpCodes.Pop); - - MethodDefinition setupMethod = SetupMethod.Find(ModuleDefinition, Logger); - if (setupMethod != null) - { - Logger.Debug($"Found setup method '{setupMethod.FullName}'", DebugLogLevel.Default); - initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder - initProcessor.Emit(OpCodes.Call, setupMethod); - initProcessor.Emit(OpCodes.Nop); - } - else - { - Logger.Debug("No setup method found", DebugLogLevel.Default); - } - - Instruction loadForBuild = Instruction.Create(OpCodes.Ldloc_0); - - initProcessor.Emit(OpCodes.Ldarg_0); - initProcessor.Emit(OpCodes.Brfalse_S, loadForBuild); - initProcessor.Emit(OpCodes.Ldarg_0); - initProcessor.Emit(OpCodes.Ldloc_0); - initProcessor.Emit(OpCodes.Callvirt, ModuleDefinition.ImportReference(Import.System.Action.Invoke.MakeGenericDeclaringType(Import.AutoDI.IApplicationBuilder.Type))); - - - initProcessor.Append(loadForBuild); - initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.Build); - initProcessor.Emit(OpCodes.Stsfld, globalServiceProvider); + partial class ProcessAssemblyTask + { + //TODO: out parameters... yuck + private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, ICodeGenerator codeGenerator, + out MethodDefinition initMethod) + { + var containerType = new TypeDefinition(Constants.Namespace, Constants.TypeName, + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed + | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) + { + BaseType = Import.System.Object + }; + + FieldDefinition globalServiceProvider = + ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); + containerType.Fields.Add(globalServiceProvider); + + MethodDefinition configureMethod = GenerateAddServicesMethod(mapping, settings, containerType, codeGenerator); + containerType.Methods.Add(configureMethod); + + initMethod = GenerateInitMethod(configureMethod, globalServiceProvider); + containerType.Methods.Add(initMethod); + + MethodDefinition disposeMethod = GenerateDisposeMethod(globalServiceProvider); + containerType.Methods.Add(disposeMethod); + + return containerType; + } + + 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 list = new List();", createListInstruction); + methodGenerator?.Append(Environment.NewLine); + } + + MethodReference funcCtor = Import.System.Func2Ctor; + + if (mapping != null) + { + int factoryIndex = 0; + var factoryMethods = new Dictionary(); + + 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) + { + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Error($"Failed to create map for {registration}\r\n{e}", additionalInformation); + } + catch (Exception e) + { + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Warning($"Failed to create map for {registration}\r\n{e}", additionalInformation); + } + } + } + + 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; + } + + private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int index, ICodeGenerator codeGenerator) + { + if (!targetType.CanMapType()) return null; + + MethodDefinition targetTypeCtor = targetType.GetMappingConstructor(); + if (targetTypeCtor == null) return null; + + var factory = new MethodDefinition($"<{targetType.Name}>_generated_{index}", + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + ModuleDefinition.ImportReference(targetType)); + factory.Parameters.Add(new ParameterDefinition("serviceProvider", ParameterAttributes.None, Import.System.IServiceProvider)); + + ILProcessor factoryProcessor = factory.Body.GetILProcessor(); + + MethodReference getServiceMethod = Import.DependencyInjection.ServiceProviderServiceExtensionsGetService; + + foreach (ParameterDefinition parameter in targetTypeCtor.Parameters) + { + factoryProcessor.Emit(OpCodes.Ldarg_0); + var genericGetService = new GenericInstanceMethod(getServiceMethod); + genericGetService.GenericArguments.Add(ModuleDefinition.ImportReference(parameter.ParameterType)); + factoryProcessor.Emit(OpCodes.Call, genericGetService); + } + + factoryProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(targetTypeCtor)); + factoryProcessor.Emit(OpCodes.Ret); + + IMethodGenerator methodGenerator = codeGenerator?.Method(factory); + if (methodGenerator != null) + { + var parameters = string.Join(", ", targetTypeCtor.Parameters.Select(x => $"serviceProvider.GetService<{x.ParameterType.NameCSharp(true)}>()")); + methodGenerator.Append($"return new {targetType.NameCSharp(true)}({parameters});", factoryProcessor.Body.Instructions.First()); + methodGenerator.Append(Environment.NewLine); + } + return factory; + } + + private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, FieldDefinition globalServiceProvider) + { + var initMethod = new MethodDefinition(Constants.InitMethodName, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); + var configureAction = new ParameterDefinition("configure", ParameterAttributes.None, Import.System.Action.Type.MakeGenericInstanceType(Import.AutoDI.IApplicationBuilder.Type)); + initMethod.Parameters.Add(configureAction); + + var applicationBuilder = new VariableDefinition(Import.AutoDI.IApplicationBuilder.Type); + initMethod.Body.Variables.Add(applicationBuilder); + ILProcessor initProcessor = initMethod.Body.GetILProcessor(); + + Instruction createApplicationbuilder = Instruction.Create(OpCodes.Newobj, Import.AutoDI.ApplicationBuilder.Ctor); + + initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); + initProcessor.Emit(OpCodes.Brfalse_S, createApplicationbuilder); + //Compare + initProcessor.Emit(OpCodes.Newobj, Import.AutoDI.Exceptions.AlreadyInitializedExceptionCtor); + initProcessor.Emit(OpCodes.Throw); + + initProcessor.Append(createApplicationbuilder); + initProcessor.Emit(OpCodes.Stloc_0); + + initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder + initProcessor.Emit(OpCodes.Ldnull); + initProcessor.Emit(OpCodes.Ldftn, configureMethod); + initProcessor.Emit(OpCodes.Newobj, ModuleDefinition.ImportReference(Import.System.Action.Ctor.MakeGenericDeclaringType(Import.DependencyInjection.IServiceCollection))); + initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.ConfigureServices); + initProcessor.Emit(OpCodes.Pop); + + MethodDefinition setupMethod = SetupMethod.Find(ModuleDefinition, Logger); + if (setupMethod != null) + { + Logger.Debug($"Found setup method '{setupMethod.FullName}'", DebugLogLevel.Default); + initProcessor.Emit(OpCodes.Ldloc_0); //applicationBuilder + initProcessor.Emit(OpCodes.Call, setupMethod); + initProcessor.Emit(OpCodes.Nop); + } + else + { + Logger.Debug("No setup method found", DebugLogLevel.Default); + } + + Instruction loadForBuild = Instruction.Create(OpCodes.Ldloc_0); + + initProcessor.Emit(OpCodes.Ldarg_0); + initProcessor.Emit(OpCodes.Brfalse_S, loadForBuild); + initProcessor.Emit(OpCodes.Ldarg_0); + initProcessor.Emit(OpCodes.Ldloc_0); + initProcessor.Emit(OpCodes.Callvirt, ModuleDefinition.ImportReference(Import.System.Action.Invoke.MakeGenericDeclaringType(Import.AutoDI.IApplicationBuilder.Type))); + + + initProcessor.Append(loadForBuild); + initProcessor.Emit(OpCodes.Callvirt, Import.AutoDI.IApplicationBuilder.Build); + initProcessor.Emit(OpCodes.Stsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); - initProcessor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Register); - - - initProcessor.Emit(OpCodes.Ret); - - return initMethod; - } - - private MethodDefinition GenerateDisposeMethod(FieldDefinition globalServiceProvider) - { - var disposeMethod = new MethodDefinition("Dispose", - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); - - VariableDefinition disposable = new VariableDefinition(Import.System.IDisposable.Type); - disposeMethod.Body.Variables.Add(disposable); - - ILProcessor processor = disposeMethod.Body.GetILProcessor(); - Instruction afterDispose = Instruction.Create(OpCodes.Nop); - - processor.Emit(OpCodes.Ldsfld, globalServiceProvider); - processor.Emit(OpCodes.Isinst, Import.System.IDisposable.Type); - processor.Emit(OpCodes.Dup); - processor.Emit(OpCodes.Stloc_0); //disposable - processor.Emit(OpCodes.Brfalse_S, afterDispose); - processor.Emit(OpCodes.Ldloc_0); //disposable - processor.Emit(OpCodes.Callvirt, Import.System.IDisposable.Dispose); - - processor.Append(afterDispose); - - processor.Emit(OpCodes.Ldsfld, globalServiceProvider); - processor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Unregister); - processor.Emit(OpCodes.Pop); - - processor.Emit(OpCodes.Ldnull); - processor.Emit(OpCodes.Stsfld, globalServiceProvider); - - processor.Emit(OpCodes.Ret); - return disposeMethod; - } + initProcessor.Emit(OpCodes.Ldsfld, globalServiceProvider); + initProcessor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Register); + + + initProcessor.Emit(OpCodes.Ret); + + return initMethod; + } + + private MethodDefinition GenerateDisposeMethod(FieldDefinition globalServiceProvider) + { + var disposeMethod = new MethodDefinition("Dispose", + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); + + VariableDefinition disposable = new VariableDefinition(Import.System.IDisposable.Type); + disposeMethod.Body.Variables.Add(disposable); + + ILProcessor processor = disposeMethod.Body.GetILProcessor(); + Instruction afterDispose = Instruction.Create(OpCodes.Nop); + + processor.Emit(OpCodes.Ldsfld, globalServiceProvider); + processor.Emit(OpCodes.Isinst, Import.System.IDisposable.Type); + processor.Emit(OpCodes.Dup); + processor.Emit(OpCodes.Stloc_0); //disposable + processor.Emit(OpCodes.Brfalse_S, afterDispose); + processor.Emit(OpCodes.Ldloc_0); //disposable + processor.Emit(OpCodes.Callvirt, Import.System.IDisposable.Dispose); + + processor.Append(afterDispose); + + processor.Emit(OpCodes.Ldsfld, globalServiceProvider); + processor.Emit(OpCodes.Call, Import.AutoDI.GlobalDI.Unregister); + processor.Emit(OpCodes.Pop); + + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Stsfld, globalServiceProvider); + + processor.Emit(OpCodes.Ret); + return disposeMethod; + } - } + } } \ No newline at end of file diff --git a/AutoDI.Build/StackTracer.cs b/AutoDI.Build/StackTracer.cs index ee5941b..1dbe2bd 100644 --- a/AutoDI.Build/StackTracer.cs +++ b/AutoDI.Build/StackTracer.cs @@ -3,33 +3,33 @@ namespace AutoDI.Build { - public static class StackTracer - { - public static AdditionalInformation GetStackTrace(Exception exception) - { - var trace = new StackTrace(exception, true); - var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; - var additionalInformation = new AdditionalInformation(); - if (reflectedType != null) - { - additionalInformation = new AdditionalInformation() - { - Column = trace.GetFrame(0).GetFileColumnNumber(), - Line = trace.GetFrame(0).GetFileLineNumber(), - MethodName = reflectedType.FullName, - File = trace.GetFrame(0).GetFileName() - }; + public static class StackTracer + { + public static AdditionalInformation GetStackTrace(Exception exception) + { + var trace = new StackTrace(exception, true); + var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; + var additionalInformation = new AdditionalInformation(); + if (reflectedType != null) + { + additionalInformation = new AdditionalInformation() + { + Column = trace.GetFrame(0).GetFileColumnNumber(), + Line = trace.GetFrame(0).GetFileLineNumber(), + MethodName = reflectedType.FullName, + File = trace.GetFrame(0).GetFileName() + }; - } - return additionalInformation; - } - } + } + return additionalInformation; + } + } - public class AdditionalInformation - { - public string MethodName { get; set; } - public string File { get; set; } - public int Line { get; set; } - public int Column { get; set; } - } + public class AdditionalInformation + { + public string MethodName { get; set; } + public string File { get; set; } + public int Line { get; set; } + public int Column { get; set; } + } } diff --git a/AutoDI.Build/TaskLogger.cs b/AutoDI.Build/TaskLogger.cs index 612c4ca..4fbdf7f 100644 --- a/AutoDI.Build/TaskLogger.cs +++ b/AutoDI.Build/TaskLogger.cs @@ -4,41 +4,41 @@ namespace AutoDI.Build { - internal class TaskLogger : ILogger - { - private readonly Task _task; + internal class TaskLogger : ILogger + { + private readonly Task _task; - public bool ErrorLogged { get; private set; } + public bool ErrorLogged { get; private set; } - public DebugLogLevel DebugLogLevel { get; set; } + public DebugLogLevel DebugLogLevel { get; set; } - private const string MessageSender = "AutoDI:"; + private const string MessageSender = "AutoDI:"; - public TaskLogger(Task task) - { - _task = task ?? throw new ArgumentNullException(nameof(task)); - } + public TaskLogger(Task task) + { + _task = task ?? throw new ArgumentNullException(nameof(task)); + } - public void Error(string message, AdditionalInformation additionalInformation) - { - ErrorLogged = true; - _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", MessageSender)); - } + public void Error(string message, AdditionalInformation additionalInformation) + { + ErrorLogged = true; + _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + } - public void Debug(string message, DebugLogLevel debugLevel) - { - if (debugLevel >= DebugLogLevel) - { - _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Low)); - } - } + public void Debug(string message, DebugLogLevel debugLevel) + { + if (debugLevel >= DebugLogLevel) + { + _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Low)); + } + } - public void Info(string message) - { - _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Normal)); - } + public void Info(string message) + { + _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Normal)); + } - public void Warning(string message, AdditionalInformation additionalInformation) + public void Warning(string message, AdditionalInformation additionalInformation) { if (additionalInformation == null) { @@ -53,5 +53,5 @@ public void Warning(string message, AdditionalInformation additionalInformation) MessageSender)); } } - } + } } \ No newline at end of file From 9c798fdc78de0b6ec5744ab5aef90d7e4ad6b014 Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Thu, 24 Oct 2019 22:47:29 -0700 Subject: [PATCH 3/3] Adding example sequence point logger --- AutoDI.Build/ILogger.cs | 5 ++-- AutoDI.Build/MultipleConstructorException.cs | 9 +++++-- AutoDI.Build/ProcessAssemblyTask.Container.cs | 10 +++++++ AutoDI.Build/StackTracer.cs | 27 +++++++++---------- AutoDI.Build/TaskLogger.cs | 3 ++- AutoDI.Build/TypeDefinitionMixins.cs | 2 +- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/AutoDI.Build/ILogger.cs b/AutoDI.Build/ILogger.cs index 9037c12..36b791f 100644 --- a/AutoDI.Build/ILogger.cs +++ b/AutoDI.Build/ILogger.cs @@ -7,7 +7,8 @@ public interface ILogger void Debug(string message, DebugLogLevel debugLevel); void Info(string message); - void Warning(string message,AdditionalInformation additionalInformation=null); - void Error(string message,AdditionalInformation additionalInformation); + void Warning(string message, AdditionalInformation additionalInformation = null); + //This could probably just replace the AdditionalInformation class with SequencePoint + void Error(string message, AdditionalInformation additionalInformation); } } \ No newline at end of file diff --git a/AutoDI.Build/MultipleConstructorException.cs b/AutoDI.Build/MultipleConstructorException.cs index 97f261b..05f851f 100644 --- a/AutoDI.Build/MultipleConstructorException.cs +++ b/AutoDI.Build/MultipleConstructorException.cs @@ -1,9 +1,14 @@ -namespace AutoDI.Build +using Mono.Cecil; + +namespace AutoDI.Build { internal class MultipleConstructorException : AutoDIBuildException { - public MultipleConstructorException(string message) : base(message) + public MethodDefinition DuplicateConstructor { get; } + + public MultipleConstructorException(string message, MethodDefinition duplicateContructor) : base(message) { + DuplicateConstructor = duplicateContructor; } } } diff --git a/AutoDI.Build/ProcessAssemblyTask.Container.cs b/AutoDI.Build/ProcessAssemblyTask.Container.cs index 4331e25..3ced4a8 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Container.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Container.cs @@ -177,6 +177,16 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set catch (MultipleConstructorException e) { var additionalInformation = StackTracer.GetStackTrace(e); + if (e.DuplicateConstructor?.DebugInformation?.HasSequencePoints == true) + { + SequencePoint sequencePoint = e.DuplicateConstructor.DebugInformation.SequencePoints.First(); + additionalInformation = new AdditionalInformation + { + File = sequencePoint.Document.Url, + Column = sequencePoint.StartColumn, + Line = sequencePoint.StartLine + }; + } Logger.Error($"Failed to create map for {registration}\r\n{e}", additionalInformation); } catch (Exception e) diff --git a/AutoDI.Build/StackTracer.cs b/AutoDI.Build/StackTracer.cs index 1dbe2bd..14d4831 100644 --- a/AutoDI.Build/StackTracer.cs +++ b/AutoDI.Build/StackTracer.cs @@ -7,27 +7,26 @@ public static class StackTracer { public static AdditionalInformation GetStackTrace(Exception exception) { - var trace = new StackTrace(exception, true); - var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; + //var trace = new StackTrace(exception, true); + //var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; var additionalInformation = new AdditionalInformation(); - if (reflectedType != null) - { - additionalInformation = new AdditionalInformation() - { - Column = trace.GetFrame(0).GetFileColumnNumber(), - Line = trace.GetFrame(0).GetFileLineNumber(), - MethodName = reflectedType.FullName, - File = trace.GetFrame(0).GetFileName() - }; - - } + //if (reflectedType != null) + //{ + // additionalInformation = new AdditionalInformation() + // { + // Column = trace.GetFrame(0).GetFileColumnNumber(), + // Line = trace.GetFrame(0).GetFileLineNumber(), + // //MethodName = reflectedType.FullName, + // File = trace.GetFrame(0).GetFileName() + // }; + // + //} return additionalInformation; } } public class AdditionalInformation { - public string MethodName { get; set; } public string File { get; set; } public int Line { get; set; } public int Column { get; set; } diff --git a/AutoDI.Build/TaskLogger.cs b/AutoDI.Build/TaskLogger.cs index 4fbdf7f..41f32a6 100644 --- a/AutoDI.Build/TaskLogger.cs +++ b/AutoDI.Build/TaskLogger.cs @@ -22,7 +22,8 @@ public TaskLogger(Task task) public void Error(string message, AdditionalInformation additionalInformation) { ErrorLogged = true; - _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, + additionalInformation.Line, additionalInformation.Column, $"{MessageSender} {message}", "", MessageSender)); } public void Debug(string message, DebugLogLevel debugLevel) diff --git a/AutoDI.Build/TypeDefinitionMixins.cs b/AutoDI.Build/TypeDefinitionMixins.cs index cadc136..3fd8c14 100644 --- a/AutoDI.Build/TypeDefinitionMixins.cs +++ b/AutoDI.Build/TypeDefinitionMixins.cs @@ -45,7 +45,7 @@ public static MethodDefinition GetMappingConstructor(this TypeDefinition targetT { if (annotatedConstructors.Length > 1) { - throw new MultipleConstructorException($"More then one constructor on '{targetType.Name}' annotated with DiConstructorAttribute"); + throw new MultipleConstructorException($"More then one constructor on '{targetType.Name}' annotated with DiConstructorAttribute", annotatedConstructors[1]); } targetTypeCtor = annotatedConstructors[0]; }