-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Revert "Reduce File I/O under the AnalyzerAssemblyLoader folder (#724… #75761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Cosifne
wants to merge
6
commits into
dotnet:main
Choose a base branch
from
Cosifne:dev/shech/revert72412In17.12
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
2132105
Revert "Reduce File I/O under the AnalyzerAssemblyLoader folder (#724…
Cosifne 1d09104
Fix unresolved conflict marks
Cosifne 7ac7221
Remove dup imports
Cosifne a34cfc5
Keep the GetBestPathHelper
Cosifne c3f09b3
bring AssemblyLoading_ResourcesInParent(AnalyzerTestKind kind) back
Cosifne 8383853
Add blank line
Cosifne File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,6 @@ | |
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Collections.Immutable; | ||
| using System.Globalization; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Reflection; | ||
|
|
@@ -48,17 +47,7 @@ internal abstract partial class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader | |
| /// <remarks> | ||
| /// Access must be guarded by <see cref="_guard"/> | ||
| /// </remarks> | ||
| private readonly Dictionary<string, (AssemblyName? AssemblyName, string RealAssemblyPath)?> _analyzerAssemblyInfoMap = new(); | ||
|
|
||
| /// <summary> | ||
| /// Mapping of analyzer dependency original full path and culture to the real satellite | ||
| /// assembly path. If the satellite assembly doesn't exist for the original analyzer and | ||
| /// culture, the real path value stored will be null. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Access must be guarded by <see cref="_guard"/> | ||
| /// </remarks> | ||
| private readonly Dictionary<(string OriginalAnalyzerPath, CultureInfo CultureInfo), string?> _analyzerSatelliteAssemblyRealPaths = new(); | ||
| private readonly Dictionary<string, (AssemblyName? AssemblyName, string RealAssemblyPath, ImmutableHashSet<string> SatelliteCultureNames)?> _analyzerAssemblyInfoMap = new(); | ||
|
|
||
| /// <summary> | ||
| /// Maps analyzer dependency simple names to the set of original full paths it was loaded from. This _only_ | ||
|
|
@@ -149,7 +138,7 @@ public void AddDependencyLocation(string fullPath) | |
| _knownAssemblyPathsBySimpleName[simpleName] = paths.Add(fullPath); | ||
| } | ||
|
|
||
| // This type assumes the file system is static for the duration of the | ||
| // This type assumses the file system is static for the duration of the | ||
| // it's instance. Repeated calls to this method, even if the underlying | ||
| // file system contents, should reuse the results of the first call. | ||
| _ = _analyzerAssemblyInfoMap.TryAdd(fullPath, null); | ||
|
|
@@ -162,7 +151,7 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
|
|
||
| CompilerPathUtilities.RequireAbsolutePath(originalAnalyzerPath, nameof(originalAnalyzerPath)); | ||
|
|
||
| (AssemblyName? assemblyName, _) = GetAssemblyInfoForPath(originalAnalyzerPath); | ||
| (AssemblyName? assemblyName, _, _) = GetAssemblyInfoForPath(originalAnalyzerPath); | ||
|
|
||
| // Not a managed assembly, nothing else to do | ||
| if (assemblyName is null) | ||
|
|
@@ -189,7 +178,7 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
| /// because we only want information for registered paths. Using unregistered paths inside the | ||
| /// implementation should result in errors. | ||
| /// </remarks> | ||
| protected (AssemblyName? AssemblyName, string RealAssemblyPath) GetAssemblyInfoForPath(string originalAnalyzerPath) | ||
| protected (AssemblyName? AssemblyName, string RealAssemblyPath, ImmutableHashSet<string> SatelliteCultureNames) GetAssemblyInfoForPath(string originalAnalyzerPath) | ||
| { | ||
| CheckIfDisposed(); | ||
|
|
||
|
|
@@ -206,7 +195,8 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
| } | ||
| } | ||
|
|
||
| string realPath = PreparePathToLoad(originalAnalyzerPath); | ||
| var resourceAssemblyCultureNames = getResourceAssemblyCultureNames(originalAnalyzerPath); | ||
| string realPath = PreparePathToLoad(originalAnalyzerPath, resourceAssemblyCultureNames); | ||
| AssemblyName? assemblyName; | ||
| try | ||
| { | ||
|
|
@@ -222,68 +212,35 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
|
|
||
| lock (_guard) | ||
| { | ||
| _analyzerAssemblyInfoMap[originalAnalyzerPath] = (assemblyName, realPath); | ||
| _analyzerAssemblyInfoMap[originalAnalyzerPath] = (assemblyName, realPath, resourceAssemblyCultureNames); | ||
| } | ||
|
|
||
| return (assemblyName, realPath); | ||
| } | ||
| return (assemblyName, realPath, resourceAssemblyCultureNames); | ||
|
|
||
| /// <summary> | ||
| /// Get the path a satellite assembly should be loaded from for the given original | ||
| /// analyzer path and culture | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This is used during assembly resolve for satellite assemblies to determine the | ||
| /// path from where the satellite assembly should be loaded for the specified culture. | ||
| /// This method calls <see cref="PrepareSatelliteAssemblyToLoad"/> to ensure this path | ||
| /// contains the satellite assembly. | ||
| /// </remarks> | ||
| internal string? GetRealSatelliteLoadPath(string originalAnalyzerPath, CultureInfo cultureInfo) | ||
| { | ||
| CheckIfDisposed(); | ||
|
|
||
| string? realSatelliteAssemblyPath = null; | ||
|
|
||
| lock (_guard) | ||
| // Discover the culture names for any satellite dlls related to this analyzer. These | ||
| // need to be understood when handling the resource loading in certain cases. | ||
| static ImmutableHashSet<string> getResourceAssemblyCultureNames(string originalAnalyzerPath) | ||
| { | ||
| if (_analyzerSatelliteAssemblyRealPaths.TryGetValue((originalAnalyzerPath, cultureInfo), out realSatelliteAssemblyPath)) | ||
| var path = Path.GetDirectoryName(originalAnalyzerPath)!; | ||
| using var enumerator = Directory.EnumerateDirectories(path, "*").GetEnumerator(); | ||
| if (!enumerator.MoveNext()) | ||
| { | ||
| return realSatelliteAssemblyPath; | ||
| return ImmutableHashSet<string>.Empty; | ||
| } | ||
| } | ||
|
|
||
| var actualCultureName = getSatelliteCultureName(originalAnalyzerPath, cultureInfo); | ||
| if (actualCultureName != null) | ||
| { | ||
| realSatelliteAssemblyPath = PrepareSatelliteAssemblyToLoad(originalAnalyzerPath, actualCultureName); | ||
| } | ||
|
|
||
| lock (_guard) | ||
| { | ||
| _analyzerSatelliteAssemblyRealPaths[(originalAnalyzerPath, cultureInfo)] = realSatelliteAssemblyPath; | ||
| } | ||
|
|
||
| return realSatelliteAssemblyPath; | ||
|
|
||
| // Discover the most specific culture name to use for the specified analyzer path and culture | ||
| static string? getSatelliteCultureName(string originalAnalyzerPath, CultureInfo cultureInfo) | ||
| { | ||
| var path = Path.GetDirectoryName(originalAnalyzerPath)!; | ||
| var resourceFileName = GetSatelliteFileName(Path.GetFileName(originalAnalyzerPath)); | ||
|
|
||
| while (cultureInfo != CultureInfo.InvariantCulture) | ||
| var builder = ImmutableHashSet.CreateBuilder<string>(StringComparer.OrdinalIgnoreCase); | ||
| do | ||
| { | ||
| var resourceFilePath = Path.Combine(path, cultureInfo.Name, resourceFileName); | ||
|
|
||
| var resourceFilePath = Path.Combine(enumerator.Current, resourceFileName); | ||
| if (File.Exists(resourceFilePath)) | ||
| { | ||
| return cultureInfo.Name; | ||
| builder.Add(Path.GetFileName(enumerator.Current)); | ||
| } | ||
|
|
||
| cultureInfo = cultureInfo.Parent; | ||
| } | ||
| while (enumerator.MoveNext()); | ||
|
|
||
| return null; | ||
| return builder.ToImmutableHashSet(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -293,6 +250,23 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
|
|
||
| return GetBestPath(assemblyName).BestOriginalPath; | ||
| } | ||
| /// <summary> | ||
| /// Get the real load path of the satellite assembly given the original path to the analyzer | ||
| /// and the desired culture name. | ||
| /// </summary> | ||
| protected string? GetSatelliteInfoForPath(string originalAnalyzerPath, string cultureName) | ||
| { | ||
| var (_, realAssemblyPath, satelliteCultureNames) = GetAssemblyInfoForPath(originalAnalyzerPath); | ||
| if (!satelliteCultureNames.Contains(cultureName)) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| var satelliteFileName = GetSatelliteFileName(Path.GetFileName(realAssemblyPath)); | ||
| var dir = Path.GetDirectoryName(realAssemblyPath)!; | ||
| return Path.Combine(dir, cultureName, satelliteFileName); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Return the best (original, real) path information for loading an assembly with the specified <see cref="AssemblyName"/>. | ||
| /// </summary> | ||
|
|
@@ -321,7 +295,7 @@ public Assembly LoadFromPath(string originalAnalyzerPath) | |
| AssemblyName? bestName = null; | ||
| foreach (var candidateOriginalPath in paths.OrderBy(StringComparer.Ordinal)) | ||
| { | ||
| (AssemblyName? candidateName, string candidateRealPath) = GetAssemblyInfoForPath(candidateOriginalPath); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I keep is referencing the |
||
| (AssemblyName? candidateName, string candidateRealPath, _) = GetAssemblyInfoForPath(candidateOriginalPath); | ||
| if (candidateName is null) | ||
| { | ||
| continue; | ||
|
|
@@ -353,18 +327,15 @@ protected static string GetSatelliteFileName(string assemblyFileName) => | |
| /// When overridden in a derived class, allows substituting an assembly path after we've | ||
| /// identified the context to load an assembly in, but before the assembly is actually | ||
| /// loaded from disk. This is used to substitute out the original path with the shadow-copied version. | ||
| /// | ||
| /// In the case the <param name="assemblyFilePath" /> is moved to a new location then | ||
| /// the resource DLLs for the specified <paramref name="resourceAssemblyCultureNames"/> must also be | ||
| /// moved _but_ retain their original relative location. | ||
| /// </summary> | ||
| protected abstract string PreparePathToLoad(string assemblyFilePath); | ||
|
|
||
| /// <summary> | ||
| /// When overridden in a derived class, allows substituting a satellite assembly path after we've | ||
| /// identified the context to load a satellite assembly in, but before the satellite assembly is actually | ||
| /// loaded from disk. This is used to substitute out the original path with the shadow-copied version. | ||
| /// </summary> | ||
| protected abstract string PrepareSatelliteAssemblyToLoad(string assemblyFilePath, string cultureName); | ||
| protected abstract string PreparePathToLoad(string assemblyFilePath, ImmutableHashSet<string> resourceAssemblyCultureNames); | ||
|
|
||
| /// <summary> | ||
| /// When <see cref="PreparePathToLoad(string)"/> is overridden this returns the most recent | ||
| /// When <see cref="PreparePathToLoad(string, ImmutableHashSet{string})"/> is overridden this returns the most recent | ||
| /// real path calculated for the <paramref name="originalFullPath"/> | ||
| /// </summary> | ||
| internal string GetRealAnalyzerLoadPath(string originalFullPath) | ||
|
|
@@ -382,6 +353,30 @@ internal string GetRealAnalyzerLoadPath(string originalFullPath) | |
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// When <see cref="PreparePathToLoad(string, ImmutableHashSet{string})"/> is overridden this returns the most recent | ||
| /// real path for the given analyzer satellite assembly path | ||
| /// </summary> | ||
| internal string? GetRealSatelliteLoadPath(string originalSatelliteFullPath) | ||
| { | ||
| // This is a satellite assembly, need to find the mapped path of the real assembly, then | ||
| // adjust that mapped path for the suffix of the satellite assembly | ||
| // | ||
| // Example of dll and it's corresponding satellite assembly | ||
| // | ||
| // c:\some\path\en-GB\util.resources.dll | ||
| // c:\some\path\util.dll | ||
| var assemblyFileName = Path.ChangeExtension(Path.GetFileNameWithoutExtension(originalSatelliteFullPath), ".dll"); | ||
|
|
||
| var assemblyDir = Path.GetDirectoryName(originalSatelliteFullPath)!; | ||
| var cultureName = Path.GetFileName(assemblyDir); | ||
| assemblyDir = Path.GetDirectoryName(assemblyDir)!; | ||
|
|
||
| // Real assembly is located in the directory above this one | ||
| var assemblyPath = Path.Combine(assemblyDir, assemblyFileName); | ||
| return GetSatelliteInfoForPath(assemblyPath, cultureName); | ||
| } | ||
|
|
||
| internal (string OriginalAssemblyPath, string RealAssemblyPath)[] GetPathMapSnapshot() | ||
| { | ||
| CheckIfDisposed(); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.