| File: Extensions\IExtensionAssemblyLoaderProvider.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Composition; using System.IO; using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Extensions; /// <summary> /// Abstraction around <see cref="IAnalyzerAssemblyLoaderProvider"/> so that we can mock that behavior in tests. /// </summary> internal interface IExtensionAssemblyLoaderProvider : IWorkspaceService { (IExtensionAssemblyLoader? assemblyLoader, Exception? extensionException) CreateNewShadowCopyLoader( string assemblyFolderPath, CancellationToken cancellationToken); } /// <summary> /// Abstraction around <see cref="IAnalyzerAssemblyLoader"/> so that we can mock that behavior in tests. /// </summary> internal interface IExtensionAssemblyLoader { Assembly LoadFromPath(string assemblyFilePath); void Unload(); } [ExportWorkspaceServiceFactory(typeof(IExtensionAssemblyLoaderProvider)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class DefaultExtensionAssemblyLoaderProviderFactory() : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) => new DefaultExtensionAssemblyLoaderProvider(workspaceServices); private sealed class DefaultExtensionAssemblyLoaderProvider(HostWorkspaceServices workspaceServices) : IExtensionAssemblyLoaderProvider { #pragma warning disable IDE0052 // Remove unread private members private readonly HostWorkspaceServices _workspaceServices = workspaceServices; #pragma warning restore IDE0052 // Remove unread private members public (IExtensionAssemblyLoader? assemblyLoader, Exception? extensionException) CreateNewShadowCopyLoader( string assemblyFolderPath, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); #if NET // These lines should always succeed. If they don't, they indicate a bug in our code that we want // to bubble out as it must be fixed. var analyzerAssemblyLoaderProvider = _workspaceServices.GetRequiredService<IAnalyzerAssemblyLoaderProvider>(); var analyzerAssemblyLoader = analyzerAssemblyLoaderProvider.CreateNewShadowCopyLoader(); // Catch exceptions here related to working with the file system. If we can't properly enumerate, // we want to report that back to the client, while not blocking the entire extension service. try { // Allow this assembly loader to load any dll in assemblyFolderPath. foreach (var dll in Directory.EnumerateFiles(assemblyFolderPath, "*.dll")) { cancellationToken.ThrowIfCancellationRequested(); try { // Check if the file is a valid .NET assembly. AssemblyName.GetAssemblyName(dll); } catch { // The file is not a valid .NET assembly, skip it. continue; } analyzerAssemblyLoader.AddDependencyLocation(dll); } return (new DefaultExtensionAssemblyLoader(analyzerAssemblyLoader), null); } catch (Exception ex) when (ex is not OperationCanceledException) { // Capture any exceptions here to be reported back in CreateAssemblyHandlersAsync. return (null, ex); } #else return default; #endif } } private sealed class DefaultExtensionAssemblyLoader( IAnalyzerAssemblyLoaderInternal assemblyLoader) : IExtensionAssemblyLoader { public void Unload() => assemblyLoader.Dispose(); public Assembly LoadFromPath(string assemblyFilePath) => assemblyLoader.LoadFromPath(assemblyFilePath); } }