/// <summary> /// Loads and returns the topological sorted list of unique assemblies to rewrite. /// </summary> internal static IEnumerable <AssemblyInfo> LoadAssembliesToRewrite(RewritingOptions options, AssemblyResolveEventHandler handler) { // Add all explicitly requested assemblies. var assemblies = new HashSet <AssemblyInfo>(); foreach (string path in options.AssemblyPaths) { if (!assemblies.Any(assembly => assembly.FilePath == path)) { var name = Path.GetFileName(path); if (options.IsAssemblyIgnored(name)) { throw new InvalidOperationException($"Rewriting assembly '{name}' ({path}) that is in the ignore list."); } assemblies.Add(new AssemblyInfo(name, path, options, handler)); } } // Find direct dependencies to each assembly and load them, if the corresponding option is enabled. foreach (var assembly in assemblies) { assembly.LoadDependencies(assemblies, handler); } // Validate that all assemblies are eligible for rewriting. foreach (var assembly in assemblies) { assembly.ValidateAssembly(); } return(SortAssemblies(assemblies)); }
/// <summary> /// Initializes a new instance of the <see cref="RewritingEngine"/> class. /// </summary> private RewritingEngine(RewritingOptions options, Configuration configuration, ILogger logger, Profiler profiler) { this.Options = options.Sanitize(); this.Configuration = configuration; this.Passes = new LinkedList <Pass>(); this.ResolveWarnings = new HashSet <string>(); this.Logger = logger; this.Profiler = profiler; }
/// <summary> /// Runs the engine using the specified rewriting options. /// </summary> internal static void Run(RewritingOptions options, Configuration configuration, Profiler profiler) { var logger = new ConsoleLogger() { LogLevel = configuration.LogLevel }; var engine = new RewritingEngine(options, configuration, logger, profiler); engine.Run(); }
/// <summary> /// Initializes a new instance of the <see cref="AssemblySignature"/> class. /// </summary> internal AssemblySignature(AssemblyInfo assembly, HashSet <AssemblyInfo> dependencies, Version rewriterVersion, RewritingOptions options) { this.FullName = assembly.FullName; this.Version = rewriterVersion.ToString(); this.Dependencies = new List <string>(dependencies.Select(dependency => dependency.FullName)); this.IsRewritingConcurrentCollections = options.IsRewritingConcurrentCollections; this.IsDataRaceCheckingEnabled = options.IsDataRaceCheckingEnabled; this.IsRewritingDependencies = options.IsRewritingDependencies; this.IsRewritingUnitTests = options.IsRewritingUnitTests; this.IsRewritingThreads = options.IsRewritingThreads; }
/// <summary> /// Initializes a new instance of the <see cref="TypeRewritingPass"/> class. /// </summary> internal TypeRewritingPass(RewritingOptions options, IEnumerable <AssemblyInfo> visitedAssemblies, ILogger logger) : base(visitedAssemblies, logger) { this.KnownTypes = new Dictionary <string, Type>(); // Populate the map with the known compiler types. this.KnownTypes[NameCache.AsyncTaskMethodBuilder] = typeof(Runtime.CompilerServices.AsyncTaskMethodBuilder); this.KnownTypes[NameCache.GenericAsyncTaskMethodBuilder] = typeof(Runtime.CompilerServices.AsyncTaskMethodBuilder <>); this.KnownTypes[NameCache.TaskAwaiter] = typeof(Runtime.CompilerServices.TaskAwaiter); this.KnownTypes[NameCache.GenericTaskAwaiter] = typeof(Runtime.CompilerServices.TaskAwaiter <>); this.KnownTypes[NameCache.ConfiguredTaskAwaitable] = typeof(Runtime.CompilerServices.ConfiguredTaskAwaitable); this.KnownTypes[NameCache.GenericConfiguredTaskAwaitable] = typeof(Runtime.CompilerServices.ConfiguredTaskAwaitable <>); this.KnownTypes[NameCache.ConfiguredTaskAwaiter] = typeof(Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter); this.KnownTypes[NameCache.GenericConfiguredTaskAwaiter] = typeof(Runtime.CompilerServices.ConfiguredTaskAwaitable <> .ConfiguredTaskAwaiter); // Populate the map with the default task-based types. this.KnownTypes[NameCache.Task] = typeof(Types.Threading.Tasks.Task); this.KnownTypes[NameCache.GenericTask] = typeof(Types.Threading.Tasks.Task <>); this.KnownTypes[NameCache.GenericTaskCompletionSource] = typeof(Types.Threading.Tasks.TaskCompletionSource <>); this.KnownTypes[NameCache.TaskExtensions] = typeof(Types.TaskExtensions); this.KnownTypes[NameCache.TaskFactory] = typeof(Types.Threading.Tasks.TaskFactory); this.KnownTypes[NameCache.GenericTaskFactory] = typeof(Types.Threading.Tasks.TaskFactory <>); this.KnownTypes[NameCache.TaskParallel] = typeof(Types.Threading.Tasks.Parallel); // Populate the map with the known synchronization types. this.KnownTypes[NameCache.Monitor] = typeof(Types.Threading.Monitor); if (options.IsRewritingConcurrentCollections) { this.KnownTypes[NameCache.ConcurrentBag] = typeof(Types.Collections.Concurrent.ConcurrentBag <>); this.KnownTypes[NameCache.ConcurrentDictionary] = typeof(Types.Collections.Concurrent.ConcurrentDictionary <,>); this.KnownTypes[NameCache.ConcurrentQueue] = typeof(Types.Collections.Concurrent.ConcurrentQueue <>); this.KnownTypes[NameCache.ConcurrentStack] = typeof(Types.Collections.Concurrent.ConcurrentStack <>); } if (options.IsDataRaceCheckingEnabled) { this.KnownTypes[NameCache.GenericList] = typeof(Types.Collections.Generic.List <>); this.KnownTypes[NameCache.GenericDictionary] = typeof(Types.Collections.Generic.Dictionary <,>); this.KnownTypes[NameCache.GenericHashSet] = typeof(Types.Collections.Generic.HashSet <>); } }
/// <summary> /// Parses the JSON configuration file and merges the options into the specified /// <see cref="RewritingOptions"/> object. /// </summary> internal static RewritingOptions ParseFromJSON(RewritingOptions options, string configurationPath) { try { // TODO: replace with the new 'System.Text.Json'. using FileStream fs = new FileStream(configurationPath, FileMode.Open, FileAccess.Read); var serializer = new DataContractJsonSerializer(typeof(JsonConfiguration)); JsonConfiguration configuration = (JsonConfiguration)serializer.ReadObject(fs); Uri baseUri = new Uri(Path.GetDirectoryName(Path.GetFullPath(configurationPath)) + Path.DirectorySeparatorChar); Uri resolvedUri = new Uri(baseUri, configuration.AssembliesPath); options.AssembliesDirectory = resolvedUri.LocalPath; if (string.IsNullOrEmpty(configuration.OutputPath)) { options.OutputDirectory = options.AssembliesDirectory; } else { resolvedUri = new Uri(baseUri, configuration.OutputPath); options.OutputDirectory = resolvedUri.LocalPath; } options.AssemblyPaths = new HashSet <string>(); if (configuration.Assemblies != null) { foreach (string assembly in configuration.Assemblies) { resolvedUri = new Uri(Path.Combine(options.AssembliesDirectory, assembly)); options.AssemblyPaths.Add(resolvedUri.LocalPath); } } options.DependencySearchPaths = configuration.DependencySearchPaths; options.IgnoredAssembliesPattern = GetDisallowedAssembliesRegex( configuration.IgnoredAssemblies ?? Array.Empty <string>()); options.IsRewritingConcurrentCollections = configuration.IsRewritingConcurrentCollections; options.IsDataRaceCheckingEnabled = configuration.IsDataRaceCheckingEnabled; options.IsRewritingDependencies = configuration.IsRewritingDependencies; options.IsRewritingUnitTests = configuration.IsRewritingUnitTests; options.IsRewritingThreads = configuration.IsRewritingThreads; } catch (Exception ex) { throw new InvalidOperationException( $"Unexpected JSON format in the '{configurationPath}' configuration file.\n{ex.Message}"); } return(options); }
/// <summary> /// Runs the engine using the specified rewriting options. /// </summary> public static void Run(Configuration configuration, RewritingOptions options) { if (string.IsNullOrEmpty(options.AssembliesDirectory)) { throw new Exception("Please provide RewritingOptions.AssembliesDirectory"); } if (string.IsNullOrEmpty(options.OutputDirectory)) { throw new Exception("Please provide RewritingOptions.OutputDirectory"); } if (options.AssemblyPaths is null || options.AssemblyPaths.Count is 0) { throw new Exception("Please provide RewritingOptions.AssemblyPaths"); } var engine = new RewritingEngine(configuration, options); engine.Run(); }
/// <summary> /// Initializes a new instance of the <see cref="AssemblyInfo"/> class. /// </summary> private AssemblyInfo(string name, string path, RewritingOptions options, AssemblyResolveEventHandler handler) { this.Name = name; this.FilePath = path; this.Dependencies = new HashSet <AssemblyInfo>(); this.Options = options; this.IsRewritten = false; this.IsDisposed = false; // TODO: can we reuse it, or do we need a new one for each assembly? var assemblyResolver = new DefaultAssemblyResolver(); // Add known search directories for resolving assemblies. assemblyResolver.AddSearchDirectory( Path.GetDirectoryName(typeof(Types.Threading.Tasks.Task).Assembly.Location)); assemblyResolver.AddSearchDirectory(this.Options.AssembliesDirectory); if (this.Options.DependencySearchPaths != null) { foreach (var dependencySearchPath in this.Options.DependencySearchPaths) { assemblyResolver.AddSearchDirectory(dependencySearchPath); } } // Add the assembly resolution error handler. assemblyResolver.ResolveFailure += handler; this.Resolver = assemblyResolver; var readerParameters = new ReaderParameters() { AssemblyResolver = assemblyResolver, ReadSymbols = this.IsSymbolFileAvailable() }; this.Definition = AssemblyDefinition.ReadAssembly(this.FilePath, readerParameters); this.FullName = this.Definition.FullName; }
/// <summary> /// Initializes a new instance of the <see cref="MethodBodyTypeRewritingPass"/> class. /// </summary> internal MethodBodyTypeRewritingPass(RewritingOptions options, IEnumerable <AssemblyInfo> visitedAssemblies, ILogger logger) : base(options, visitedAssemblies, logger) { }
/// <summary> /// Initializes a new instance of the <see cref="RewritingEngine"/> class. /// </summary> /// <param name="configuration">The test configuration to use when rewriting unit tests.</param> /// <param name="options">The <see cref="RewritingOptions"/> for this rewriter.</param> private RewritingEngine(Configuration configuration, RewritingOptions options) { this.Configuration = configuration; this.Options = options; this.Logger = options.Logger ?? new ConsoleLogger() { LogLevel = options.LogLevel }; this.Profiler = new Profiler(); this.RewrittenAssemblies = new Dictionary <string, AssemblyNameDefinition>(); var ignoredAssemblies = options.IgnoredAssemblies ?? Array.Empty <string>(); StringBuilder combined = new StringBuilder(); foreach (var e in this.DefaultDisallowedList.Concat(ignoredAssemblies)) { combined.Append(combined.Length is 0 ? "(" : "|"); combined.Append(e); } combined.Append(')'); try { this.DisallowedAssemblies = new Regex(combined.ToString()); } catch (Exception ex) { throw new Exception("DisallowedAssemblies not a valid regular expression\n" + ex.Message); } this.RewritingPasses = new List <AssemblyRewriter>() { new TaskRewriter(this.Logger), new MonitorRewriter(this.Logger), new ExceptionFilterRewriter(this.Logger) }; if (this.Options.IsRewritingThreads) { this.RewritingPasses.Add(new ThreadingRewriter(this.Logger)); } if (this.Options.IsDataRaceCheckingEnabled) { this.RewritingPasses.Add(new DataRaceCheckingRewriter(this.Logger)); } if (this.Options.IsRewritingUnitTests) { // We are running this pass last, as we are rewriting the original method, and // we need the other rewriting passes to happen before this pass. this.RewritingPasses.Add(new MSTestRewriter(this.Configuration, this.Logger)); } this.RewritingPasses.Add(new AssertionInjectionRewriter(this.Logger)); this.RewritingPasses.Add(new NotSupportedInvocationRewriter(this.Logger)); // expand folder if (this.Options.AssemblyPaths is null || this.Options.AssemblyPaths.Count is 0) { // Expand to include all .dll files in AssemblyPaths. foreach (var file in Directory.GetFiles(this.Options.AssembliesDirectory, "*.dll")) { if (this.IsDisallowed(Path.GetFileName(file))) { this.Options.AssemblyPaths.Add(file); } else { Debug.WriteLine("Skipping " + file); } } } }