Skip to content

Commit c5401f4

Browse files
committed
more
1 parent c584af9 commit c5401f4

File tree

2 files changed

+40
-97
lines changed

2 files changed

+40
-97
lines changed

src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,98 +27,67 @@ internal sealed class RazorAnalyzerAssemblyResolver() : IAnalyzerAssemblyResolve
2727

2828
internal static readonly ImmutableArray<string> RazorAssemblyNames = [RazorCompilerAssemblyName, RazorUtilsAssemblyName, ObjectPoolAssemblyName];
2929

30-
public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory)
31-
{
32-
if (assemblyName.Name is not (RazorCompilerAssemblyName or RazorUtilsAssemblyName or ObjectPoolAssemblyName))
33-
{
34-
return null;
35-
}
36-
37-
return Resolve(loader.CompilerLoadContext, assemblyName, directory);
38-
}
30+
public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) =>
31+
ResolveCore(loader.CompilerLoadContext, assemblyName, directory);
3932

40-
public static Assembly? ResolveRazorAssembly(AssemblyName assemblyName, string rootDirectory)
41-
{
42-
var compilerLoadContext = AssemblyLoadContext.GetLoadContext(typeof(Microsoft.CodeAnalysis.Compilation).Assembly)!;
43-
return Resolve(compilerLoadContext, assemblyName, rootDirectory);
44-
}
33+
public static Assembly? ResolveRazorAssembly(AssemblyName assemblyName, string rootDirectory) =>
34+
ResolveCore(
35+
AssemblyLoadContext.GetLoadContext(typeof(Microsoft.CodeAnalysis.Compilation).Assembly)!,
36+
assemblyName,
37+
rootDirectory);
4538

4639
/// <summary>
4740
/// This will resolve the razor generator assembly specified by <paramref name="assemblyName"/> in the specified
4841
/// <paramref name="compilerLoadContext"/>.
49-
///
50-
/// This will resolve _all_ of the known razor generator assemblies, not just the specified one. This is necessary
51-
/// because the compiler load context, unlike the directory load context, does not have a way to resolve dependencies.
52-
/// That means if we loaded Microsoft.CodeAnalysis.Razor.Compiler.dll and nothing else it's possible we'd eventually
53-
/// get an exception when it tried to access members in Microsoft.AspNetCore.Razor.Utilities.Shared.dll.
5442
/// </summary>
55-
internal static Assembly? Resolve(AssemblyLoadContext compilerLoadContext, AssemblyName assemblyName, string directory)
43+
internal static Assembly? ResolveCore(AssemblyLoadContext compilerLoadContext, AssemblyName assemblyName, string directory)
5644
{
57-
Debug.Assert(assemblyName.Name is not null);
58-
59-
var found = false;
60-
Assembly? resolvedAssembly = null;
61-
foreach (var name in RazorAssemblyNames)
45+
if (assemblyName.Name is not (RazorCompilerAssemblyName or RazorUtilsAssemblyName or ObjectPoolAssemblyName))
6246
{
63-
var assembly = resolve(compilerLoadContext, new AssemblyName(name), directory);
64-
if (name == assemblyName.Name)
65-
{
66-
found = true;
67-
resolvedAssembly = assembly;
68-
}
47+
return null;
6948
}
7049

71-
if (!found)
50+
var assembly = compilerLoadContext.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name);
51+
if (assembly is not null)
7252
{
73-
resolvedAssembly = resolve(compilerLoadContext, assemblyName, directory);
53+
return assembly;
7454
}
7555

76-
return resolvedAssembly;
77-
78-
static Assembly? resolve(AssemblyLoadContext compilerLoadContext, AssemblyName assemblyName, string directory)
56+
var assemblyFileName = $"{assemblyName.Name}.dll";
57+
var assemblyPath = Path.Combine(directory, assemblyFileName);
58+
if (File.Exists(assemblyPath))
7959
{
80-
var assembly = compilerLoadContext.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name);
81-
if (assembly is not null)
60+
// https://github.com/dotnet/roslyn/issues/76868
61+
//
62+
// There is a subtle race condition in this logic as another thread could load the assembly inbetween
63+
// the above calls and this one. Short term will just catch and grab the loaded assembly but longer
64+
// term need to think about creating a dedicated AssemblyLoadContext for the razor assemblies
65+
// which avoids this race condition.
66+
try
8267
{
83-
return assembly;
68+
assembly = compilerLoadContext.LoadFromAssemblyPath(assemblyPath);
8469
}
85-
86-
var assemblyFileName = $"{assemblyName.Name}.dll";
87-
var assemblyPath = Path.Combine(directory, assemblyFileName);
88-
if (File.Exists(assemblyPath))
70+
catch
71+
{
72+
assembly = compilerLoadContext.Assemblies.Single(a => a.GetName().Name == assemblyName.Name);
73+
}
74+
}
75+
else
76+
{
77+
// There are assemblies in the razor sdk generator directory that do not exist in the VS installation. That
78+
// means when the paths are redirected, it's possible that the assembly is not found. In that case, we should
79+
// load the assembly from the VS installation by querying through the compiler context.
80+
try
8981
{
90-
// https://github.com/dotnet/roslyn/issues/76868
91-
//
92-
// There is a subtle race condition in this logic as another thread could load the assembly inbetween
93-
// the above calls and this one. Short term will just catch and grab the loaded assembly but longer
94-
// term need to think about creating a dedicated AssemblyLoadContext for the razor assemblies
95-
// which avoids this race condition.
96-
try
97-
{
98-
assembly = compilerLoadContext.LoadFromAssemblyPath(assemblyPath);
99-
}
100-
catch
101-
{
102-
assembly = compilerLoadContext.Assemblies.Single(a => a.GetName().Name == assemblyName.Name);
103-
}
82+
assembly = compilerLoadContext.LoadFromAssemblyName(assemblyName);
10483
}
105-
else
84+
catch (FileNotFoundException)
10685
{
107-
// There are assemblies in the razor sdk generator directory that do not exist in the VS installation. That
108-
// means when the paths are redirected, it's possible that the assembly is not found. In that case, we should
109-
// load the assembly from the VS installation by querying through the compiler context.
110-
try
111-
{
112-
assembly = compilerLoadContext.LoadFromAssemblyName(assemblyName);
113-
}
114-
catch (FileNotFoundException)
115-
{
116-
assembly = null;
117-
}
86+
assembly = null;
11887
}
119-
120-
return assembly;
12188
}
89+
90+
return assembly;
12291
}
12392
}
12493
}

src/Tools/ExternalAccess/RazorTest/RazorAnalyzerAssemblyResolverTests.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -86,32 +86,6 @@ private static void RunWithLoader(Action<RazorAnalyzerAssemblyResolver, Analyzer
8686
[Obsolete]
8787
internal static RazorAnalyzerAssemblyResolver CreateResolver() => new RazorAnalyzerAssemblyResolver();
8888

89-
[Fact]
90-
public void OneLoadsAll()
91-
{
92-
var dir = TempRoot.CreateDirectory().Path;
93-
CreateRazorAssemblies(dir);
94-
foreach (var name in RazorAnalyzerAssemblyResolver.RazorAssemblyNames)
95-
{
96-
RunWithLoader((resolver, loader, currentLoadContext) =>
97-
{
98-
var assembly = resolver.Resolve(
99-
loader,
100-
new AssemblyName(name),
101-
currentLoadContext,
102-
dir);
103-
Assert.NotNull(assembly);
104-
Assert.Equal(name, assembly.GetName().Name);
105-
106-
foreach (var n in RazorAnalyzerAssemblyResolver.RazorAssemblyNames)
107-
{
108-
var a = loader.CompilerLoadContext.Assemblies.Single(x => x.GetName().Name == n);
109-
Assert.NotNull(a);
110-
}
111-
});
112-
}
113-
}
114-
11589
/// <summary>
11690
/// When running in Visual Studio the razor generator will be redirected to the razor language
11791
/// services directory. That will not contain all of the necessary DLLs. Anything that is a

0 commit comments

Comments
 (0)