-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathAssemblyCache.cs
More file actions
145 lines (122 loc) · 4.94 KB
/
Copy pathAssemblyCache.cs
File metadata and controls
145 lines (122 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System.Collections.Generic;
using System.Linq;
using Semmle.Util.Logging;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
/// <summary>
/// Manages the set of assemblies.
/// Searches for assembly DLLs, indexes them and provides
/// a lookup facility from assembly ID to filename.
/// </summary>
internal class AssemblyCache
{
/// <summary>
/// Locate all reference files and index them.
/// </summary>
/// <param name="paths">
/// Paths to search. Directories are searched recursively. Files are added directly to the
/// assembly cache.
/// </param>
/// <param name="logger">Callback for progress.</param>
public AssemblyCache(IEnumerable<AssemblyLookupLocation> paths, IEnumerable<string> frameworkPaths, ILogger logger)
{
this.logger = logger;
foreach (var path in paths)
{
dllsToIndex.AddRange(path.GetDlls(logger));
}
IndexReferences(frameworkPaths);
}
/// <summary>
/// Indexes all DLLs we have located.
/// Because this is a potentially time-consuming operation, it is put into a separate stage.
/// </summary>
private void IndexReferences(IEnumerable<string> frameworkPaths)
{
logger.LogInfo($"Indexing {dllsToIndex.Count} assemblies...");
// Read all of the files
foreach (var filename in dllsToIndex)
{
IndexReference(filename);
}
logger.LogInfo($"Read {assemblyInfoByFileName.Count} assembly infos");
foreach (var info in assemblyInfoByFileName.Values
.OrderBy(info => info.Name)
.OrderAssemblyInfosByPreference(frameworkPaths))
{
foreach (var index in info.IndexStrings)
{
assemblyInfoById[index] = info;
}
}
}
private void IndexReference(string filename)
{
try
{
logger.LogDebug($"Reading assembly info from {filename}");
var info = AssemblyInfo.ReadFromFile(filename);
assemblyInfoByFileName[filename] = info;
}
catch (AssemblyLoadException)
{
logger.LogInfo($"Couldn't read assembly info from {filename}");
}
}
/// <summary>
/// Given an assembly id, determine its full info.
/// </summary>
/// <param name="id">The given assembly id.</param>
/// <returns>The information about the assembly.</returns>
public AssemblyInfo ResolveReference(string id)
{
// Fast path if we've already seen this before.
if (failedAssemblyInfoIds.Contains(id))
throw new AssemblyLoadException();
(id, var assemblyName) = AssemblyInfo.ComputeSanitizedAssemblyInfo(id);
// Look up the id in our references map.
if (assemblyInfoById.TryGetValue(id, out var result))
{
// The string is in the references map.
return result;
}
// Fallback position - locate the assembly by its lower-case name only.
var asmName = assemblyName.ToLowerInvariant();
if (assemblyInfoById.TryGetValue(asmName, out result))
{
assemblyInfoById[asmName] = result; // Speed up the next time the same string is resolved
return result;
}
failedAssemblyInfoIds.Add(id); // Fail early next time
throw new AssemblyLoadException();
}
/// <summary>
/// All the assemblies we have indexed.
/// </summary>
public IEnumerable<AssemblyInfo> AllAssemblies => assemblyInfoByFileName.Select(a => a.Value);
/// <summary>
/// Retrieve the assembly info of a pre-cached assembly.
/// </summary>
/// <param name="filepath">The filename to query.</param>
/// <returns>The assembly info.</returns>
public AssemblyInfo GetAssemblyInfo(string filepath)
{
if (assemblyInfoByFileName.TryGetValue(filepath, out var info))
{
return info;
}
IndexReference(filepath);
if (assemblyInfoByFileName.TryGetValue(filepath, out info))
{
return info;
}
throw new AssemblyLoadException();
}
private readonly List<string> dllsToIndex = new List<string>();
private readonly Dictionary<string, AssemblyInfo> assemblyInfoByFileName = [];
// Map from assembly id (in various formats) to the full info.
private readonly Dictionary<string, AssemblyInfo> assemblyInfoById = [];
private readonly HashSet<string> failedAssemblyInfoIds = [];
private readonly ILogger logger;
}
}