Skip to content

Commit c14d291

Browse files
authored
Merge pull request #54 from jnm2/faster_resolver
Use stripped-down resolver for perf (no GAC, deferred reading mode)
2 parents 4951f6e + bc2840b commit c14d291

File tree

5 files changed

+109
-12
lines changed

5 files changed

+109
-12
lines changed

CecilBasedAnnotator/Program.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33

44
namespace CecilBasedAnnotator
55
{
6+
using System.Collections.Immutable;
7+
68
internal class Program
79
{
810
private static void Main(string[] args)
911
{
10-
TunnelVisionLabs.ReferenceAssemblyAnnotator.Program.Main(log: null, args[0], args[1], args[2]);
12+
TunnelVisionLabs.ReferenceAssemblyAnnotator.Program.Main(
13+
log: null,
14+
referenceAssembly: args[0],
15+
targetFrameworkDirectories: args[1].Split(';').ToImmutableArray(),
16+
annotatedReferenceAssembly: args[2],
17+
outputAssembly: args[3]);
1118
}
1219
}
1320
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"profiles": {
3-
"mscorlib": {
3+
"Microsoft.Win32.Primitives (netstandard1.6)": {
44
"commandName": "Project",
5-
"commandLineArgs": "\"$(NuGetPackageRoot)\\microsoft.netframework.referenceassemblies.net45\\1.0.0-preview.2\\build\\.NETFramework\\v4.5\\mscorlib.dll\" \"$(NuGetPackageRoot)\\microsoft.netcore.app.ref\\3.0.0-preview8-28405-07\\ref\\netcoreapp3.0\\mscorlib.dll\" mscorlib2.dll"
5+
"commandLineArgs": "\"$(NuGetPackageRoot)\\microsoft.win32.primitives\\4.3.0\\ref\\netstandard1.3\\Microsoft.Win32.Primitives.dll\" \"$(NuGetPackageRoot)\\microsoft.win32.primitives\\4.3.0\\ref\\netstandard1.3\\;$(NuGetPackageRoot)\\system.runtime\\4.3.0\\ref\\netstandard1.5\\\\\" \"$(NuGetPackageRoot)\\microsoft.netcore.app.ref\\3.0.0\\ref\\netcoreapp3.0\\Microsoft.Win32.Primitives.dll\" Microsoft.Win32.Primitives.dll"
66
},
7-
"System": {
7+
"System.ComponentModel.DataAnnotations (netcoreapp2.0)": {
88
"commandName": "Project",
9-
"commandLineArgs": "\"$(NuGetPackageRoot)\\microsoft.netframework.referenceassemblies.net45\\1.0.0-preview.2\\build\\.NETFramework\\v4.5\\System.dll\" \"$(NuGetPackageRoot)\\microsoft.netcore.app.ref\\3.0.0-preview8-28405-07\\ref\\netcoreapp3.0\\System.dll\" System2.dll"
9+
"commandLineArgs": "\"$(NuGetPackageRoot)\\microsoft.netcore.app\\2.0.0\\ref\\netcoreapp2.0\\System.ComponentModel.DataAnnotations.dll\" \"$(NuGetPackageRoot)\\microsoft.netcore.app\\2.0.0\\ref\\netcoreapp2.0\\\\\" \"$(NuGetPackageRoot)\\microsoft.netcore.app.ref\\3.0.0\\ref\\netcoreapp3.0\\System.ComponentModel.DataAnnotations.dll\" System.ComponentModel.DataAnnotations.dll"
1010
}
1111
}
12-
}
12+
}

TunnelVisionLabs.ReferenceAssemblyAnnotator/AnnotatorBuildTask.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace TunnelVisionLabs.ReferenceAssemblyAnnotator
55
{
66
using System;
7+
using System.Collections.Immutable;
78
using System.IO;
89
using System.Linq;
910
using Microsoft.Build.Framework;
@@ -96,7 +97,14 @@ public override bool Execute()
9697

9798
Directory.CreateDirectory(OutputPath);
9899
string outputAssembly = Path.Combine(OutputPath, Path.GetFileName(unannotatedReferenceAssembly));
99-
Program.Main(log, unannotatedReferenceAssembly, annotatedReferenceAssembly, outputAssembly);
100+
101+
Program.Main(
102+
log,
103+
unannotatedReferenceAssembly,
104+
TargetFrameworkDirectories.Select(item => item.ItemSpec).ToImmutableArray(),
105+
annotatedReferenceAssembly,
106+
outputAssembly);
107+
100108
GeneratedAssemblies = new[] { new TaskItem(outputAssembly) };
101109

102110
string sourceDocumentation = Path.ChangeExtension(unannotatedReferenceAssembly, ".xml");
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace TunnelVisionLabs.ReferenceAssemblyAnnotator
5+
{
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.IO;
9+
using System.Linq;
10+
using Mono.Cecil;
11+
12+
internal sealed class AssemblyResolver : IAssemblyResolver
13+
{
14+
private static readonly ReaderParameters DefaultReaderParameters = new ReaderParameters(ReadingMode.Deferred);
15+
16+
private readonly ImmutableArray<string> _searchDirectories;
17+
private readonly Dictionary<string, AssemblyDefinition?> _assembliesByFileName = new Dictionary<string, AssemblyDefinition?>();
18+
19+
public AssemblyResolver(ImmutableArray<string> searchDirectories)
20+
{
21+
_searchDirectories = searchDirectories;
22+
}
23+
24+
public AssemblyDefinition? Resolve(AssemblyNameReference name)
25+
{
26+
return Resolve(name, DefaultReaderParameters);
27+
}
28+
29+
public AssemblyDefinition? Resolve(AssemblyNameReference name, ReaderParameters parameters)
30+
{
31+
if (!_assembliesByFileName.TryGetValue(name.Name, out var assembly))
32+
{
33+
var stream = _searchDirectories
34+
.Select(directory => OpenReadIfExists(Path.Combine(directory, name.Name + ".dll")))
35+
.FirstOrDefault(s => s is object);
36+
37+
assembly = stream is null ? null : AssemblyDefinition.ReadAssembly(stream, parameters);
38+
39+
_assembliesByFileName.Add(name.Name, assembly);
40+
}
41+
42+
return assembly is object && Matches(assembly.Name, name) ? assembly : null;
43+
}
44+
45+
private static FileStream? OpenReadIfExists(string path)
46+
{
47+
try
48+
{
49+
return File.OpenRead(path);
50+
}
51+
catch (FileNotFoundException)
52+
{
53+
return null;
54+
}
55+
}
56+
57+
/// <summary>
58+
/// The point of this method is to be a high-performance sanity check, not to reproduce .NET assembly loading
59+
/// behavior.
60+
/// </summary>
61+
private static bool Matches(AssemblyNameDefinition definition, AssemblyNameReference reference)
62+
{
63+
return definition.Name == reference.Name
64+
&& definition.Version >= reference.Version;
65+
}
66+
67+
public void Dispose()
68+
{
69+
foreach (var loadedAssembly in _assembliesByFileName.Values)
70+
{
71+
loadedAssembly?.Dispose();
72+
}
73+
74+
_assembliesByFileName.Clear();
75+
}
76+
}
77+
}

TunnelVisionLabs.ReferenceAssemblyAnnotator/Program.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace TunnelVisionLabs.ReferenceAssemblyAnnotator
55
{
66
using System;
77
using System.Collections.Generic;
8+
using System.Collections.Immutable;
89
using System.IO;
910
using System.Linq;
1011
using System.Runtime.CompilerServices;
@@ -13,10 +14,9 @@ namespace TunnelVisionLabs.ReferenceAssemblyAnnotator
1314

1415
internal class Program
1516
{
16-
internal static void Main(SuppressibleLoggingHelper? log, string referenceAssembly, string annotatedReferenceAssembly, string outputAssembly)
17+
internal static void Main(SuppressibleLoggingHelper? log, string referenceAssembly, ImmutableArray<string> targetFrameworkDirectories, string annotatedReferenceAssembly, string outputAssembly)
1718
{
18-
var assemblyResolver = new DefaultAssemblyResolver();
19-
assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(referenceAssembly));
19+
using var assemblyResolver = new AssemblyResolver(targetFrameworkDirectories);
2020
using var assemblyDefinition = AssemblyDefinition.ReadAssembly(referenceAssembly, new ReaderParameters(ReadingMode.Deferred) { AssemblyResolver = assemblyResolver });
2121

2222
foreach (var module in assemblyDefinition.Modules)
@@ -26,10 +26,15 @@ internal static void Main(SuppressibleLoggingHelper? log, string referenceAssemb
2626
log?.LogWarning("RA1000", "Skipping mixed-mode implementation assembly '{0}'", assemblyDefinition.Name);
2727
return;
2828
}
29+
30+
if (module.TypeSystem.Object.Resolve() is null)
31+
{
32+
log?.LogWarning("RA1001", "Cannot resolve core library for assembly '{0}', skipping", assemblyDefinition.Name);
33+
return;
34+
}
2935
}
3036

31-
var annotatedAssemblyResolver = new DefaultAssemblyResolver();
32-
annotatedAssemblyResolver.AddSearchDirectory(Path.GetDirectoryName(annotatedReferenceAssembly));
37+
using var annotatedAssemblyResolver = new AssemblyResolver(ImmutableArray.Create(Path.GetDirectoryName(Path.GetFullPath(annotatedReferenceAssembly))!));
3338
using var annotatedAssemblyDefinition = AssemblyDefinition.ReadAssembly(annotatedReferenceAssembly, new ReaderParameters(ReadingMode.Deferred) { AssemblyResolver = annotatedAssemblyResolver });
3439

3540
var wellKnownTypes = new WellKnownTypes(assemblyDefinition);

0 commit comments

Comments
 (0)