/// <summary> /// Extract an assembly to a new trap file. /// If the trap file exists, skip extraction to avoid duplicating /// extraction within the snapshot. /// </summary> /// <param name="r">The assembly to extract.</param> void DoAnalyseAssembly(PortableExecutableReference r) { try { var stopwatch = new Stopwatch(); stopwatch.Start(); var assemblyPath = r.FilePath; var projectLayout = layout.LookupProjectOrDefault(assemblyPath); using (var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true)) { var skipExtraction = FileIsCached(assemblyPath, trapWriter.TrapFile); if (!skipExtraction) { /* Note on parallel builds: * * The trap writer and source archiver both perform atomic moves * of the file to the final destination. * * If the same source file or trap file are generated concurrently * (by different parallel invocations of the extractor), then * last one wins. * * Specifically, if two assemblies are analysed concurrently in a build, * then there is a small amount of duplicated work but the output should * still be correct. */ // compilation.Clone() reduces memory footprint by allowing the symbols // in c to be garbage collected. Compilation c = compilation.Clone(); var assembly = c.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol; if (assembly != null) { var cx = new Context(extractor, c, trapWriter, new AssemblyScope(assembly, assemblyPath)); foreach (var module in assembly.Modules) { AnalyseNamespace(cx, module.GlobalNamespace); } cx.PopulateAll(); } } ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted); } } catch (Exception ex) { Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", r.FilePath, ex); } }
void DoAnalyseCompilation(string cwd, string[] args) { try { var assemblyPath = extractor.OutputPath; var assembly = compilation.Assembly; var projectLayout = layout.LookupProjectOrDefault(assemblyPath); var trapWriter = projectLayout.CreateTrapWriter(Logger, assemblyPath, true, options.TrapCompression); compilationTrapFile = trapWriter; // Dispose later var cx = extractor.CreateContext(compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath, true)); compilationEntity = new Entities.Compilation(cx, cwd, args); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", "compilation", ex); } }
private void DoAnalyseCompilation() { try { var assemblyPath = extractor.OutputPath; var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); var assembly = compilation.Assembly; var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false); compilationTrapFile = trapWriter; // Dispose later var cx = new Context(extractor, compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath), AddAssemblyTrapPrefix); compilationEntity = Entities.Compilation.Create(cx); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", "compilation", ex); } }
/// <summary> /// Injects the custom <see cref="CSharpCompilation"/> to the emitting process, /// and resumes it. /// </summary> /// <seealso href="http://source.roslyn.io/#Microsoft.CodeAnalysis/Compilation/Compilation.cs,42341c66e909e676"/> private static void CheckOptionsAndCreateModuleBuilder(RedirectionContext context) { // Sender is a CSharpCompilation CSharpCompilation compilation = (CSharpCompilation)context.Sender; CSharpCompilation clone = compilation.Clone(); // First argument is a DiagnosticBag object diagnosticBag = context.Arguments[0]; Action <Diagnostic> addDiagnostic = Helpers.MakeAddDiagnostic(diagnosticBag); Func <IEnumerable <Diagnostic> > getDiagnostics = Helpers.MakeGetDiagnostics(diagnosticBag); object GetOriginal(CSharpCompilation newCompilation) { object[] args = new object[context.Arguments.Count]; context.Arguments.CopyTo(args, 0); newCompilation.CopyTo(compilation); return(context.Invoke(args)); } // CancellationToken should be last argument, but whatever. CancellationToken cancellationToken = context.Arguments.OfType <CancellationToken>().FirstOrDefault(); // Edit the compilation (if a matching CometaryManager is found) CompilationRedirection.Stop(); using (CompilationProcessor manager = CompilationProcessor.Create(GetOriginal, addDiagnostic, getDiagnostics)) { manager.RegisterAttributes(compilation.Assembly); // Edit the compilation, and emit it. if (manager.TryEditCompilation(compilation, cancellationToken, out CSharpCompilation _, out object moduleBuilder)) { // No error, we can keep going context.ReturnValue = moduleBuilder; addDiagnostic(Diagnostic.Create( id: "ProcessSuccess", category: Common.DiagnosticsCategory, message: "Successfully edited the emitted compilation.", severity: DiagnosticSeverity.Info, defaultSeverity: DiagnosticSeverity.Info, isEnabledByDefault: true, warningLevel: -1, isSuppressed: false)); }
private static CSharpCompilation GetCompilationWithExternAliases(CSharpCompilation compilation, ImmutableArray<ExternAliasRecord> externAliasRecords) { if (externAliasRecords.IsDefaultOrEmpty) { return compilation.Clone(); } var updatedReferences = ArrayBuilder<MetadataReference>.GetInstance(); var assembliesAndModulesBuilder = ArrayBuilder<Symbol>.GetInstance(); foreach (var reference in compilation.References) { updatedReferences.Add(reference); assembliesAndModulesBuilder.Add(compilation.GetAssemblyOrModuleSymbol(reference)); } Debug.Assert(assembliesAndModulesBuilder.Count == updatedReferences.Count); var assembliesAndModules = assembliesAndModulesBuilder.ToImmutableAndFree(); foreach (var externAliasRecord in externAliasRecords) { var targetAssembly = externAliasRecord.TargetAssembly as AssemblySymbol; int index; if (targetAssembly != null) { index = assembliesAndModules.IndexOf(targetAssembly); } else { index = IndexOfMatchingAssembly((AssemblyIdentity)externAliasRecord.TargetAssembly, assembliesAndModules, compilation.Options.AssemblyIdentityComparer); } if (index < 0) { Debug.WriteLine($"Unable to find corresponding assembly reference for extern alias '{externAliasRecord}'"); continue; } var externAlias = externAliasRecord.Alias; var assemblyReference = updatedReferences[index]; var oldAliases = assemblyReference.Properties.Aliases; var newAliases = oldAliases.IsEmpty ? ImmutableArray.Create(MetadataReferenceProperties.GlobalAlias, externAlias) : oldAliases.Concat(ImmutableArray.Create(externAlias)); // NOTE: Dev12 didn't emit custom debug info about "global", so we don't have // a good way to distinguish between a module aliased with both (e.g.) "X" and // "global" and a module aliased with only "X". As in Dev12, we assume that // "global" is a valid alias to remain at least as permissive as source. // NOTE: In the event that this introduces ambiguities between two assemblies // (e.g. because one was "global" in source and the other was "X"), it should be // possible to disambiguate as long as each assembly has a distinct extern alias, // not necessarily used in source. Debug.Assert(newAliases.Contains(MetadataReferenceProperties.GlobalAlias)); // Replace the value in the map with the updated reference. updatedReferences[index] = assemblyReference.WithAliases(newAliases); } compilation = compilation.WithReferences(updatedReferences); updatedReferences.Free(); return compilation; }
private static CSharpCompilation GetCompilationWithExternAliases(CSharpCompilation compilation, ImmutableArray<string> externAliasStrings) { if (externAliasStrings.IsDefaultOrEmpty) { return compilation.Clone(); } var updatedReferences = ArrayBuilder<MetadataReference>.GetInstance(); var assemblyIdentities = ArrayBuilder<AssemblyIdentity>.GetInstance(); foreach (var reference in compilation.References) { var identity = reference.Properties.Kind == MetadataImageKind.Assembly ? ((AssemblySymbol)compilation.GetAssemblyOrModuleSymbol(reference)).Identity : null; assemblyIdentities.Add(identity); updatedReferences.Add(reference); } Debug.Assert(assemblyIdentities.Count == updatedReferences.Count); var comparer = compilation.Options.AssemblyIdentityComparer; var numAssemblies = assemblyIdentities.Count; foreach (var externAliasString in externAliasStrings) { string alias; string externAlias; string target; ImportTargetKind kind; if (!CustomDebugInfoReader.TryParseCSharpImportString(externAliasString, out alias, out externAlias, out target, out kind)) { Debug.WriteLine("Unable to parse extern alias '{0}'", (object)externAliasString); continue; } Debug.Assert(kind == ImportTargetKind.Assembly, "Programmer error: How did a non-assembly get in the extern alias list?"); Debug.Assert(alias == null); // Not used. Debug.Assert(externAlias != null); // Name of the extern alias. Debug.Assert(target != null); // Name of the target assembly. AssemblyIdentity targetIdentity; if (!AssemblyIdentity.TryParseDisplayName(target, out targetIdentity)) { Debug.WriteLine("Unable to parse target of extern alias '{0}'", (object)externAliasString); continue; } int index = -1; for (int i = 0; i < numAssemblies; i++) { var candidateIdentity = assemblyIdentities[i]; if (candidateIdentity != null && comparer.ReferenceMatchesDefinition(targetIdentity, candidateIdentity)) { index = i; break; } } if (index < 0) { Debug.WriteLine("Unable to find corresponding assembly reference for extern alias '{0}'", (object)externAliasString); continue; } var assemblyReference = updatedReferences[index]; var oldAliases = assemblyReference.Properties.Aliases; var newAliases = oldAliases.IsEmpty ? ImmutableArray.Create(MetadataReferenceProperties.GlobalAlias, externAlias) : oldAliases.Concat(ImmutableArray.Create(externAlias)); // NOTE: Dev12 didn't emit custom debug info about "global", so we don't have // a good way to distinguish between a module aliased with both (e.g.) "X" and // "global" and a module aliased with only "X". As in Dev12, we assume that // "global" is a valid alias to remain at least as permissive as source. // NOTE: In the event that this introduces ambiguities between two assemblies // (e.g. because one was "global" in source and the other was "X"), it should be // possible to disambiguate as long as each assembly has a distinct extern alias, // not necessarily used in source. Debug.Assert(newAliases.Contains(MetadataReferenceProperties.GlobalAlias)); // Replace the value in the map with the updated reference. updatedReferences[index] = assemblyReference.WithAliases(newAliases); } compilation = compilation.WithReferences(updatedReferences); assemblyIdentities.Free(); updatedReferences.Free(); return compilation; }