Backend/RiderPlugin/ForTea.RiderPlugin/TemplateProcessing/CodeGeneration/Reference/Impl/T4LowLevelReferenceExtractionManager.cs (110 lines of code) (raw):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using GammaJul.ForTea.Core.TemplateProcessing.CodeGeneration.Reference;
using JetBrains.Annotations;
using JetBrains.Application.Infra;
using JetBrains.Application.Parts;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.Metadata.Reader.API;
using JetBrains.Metadata.Reader.Impl;
using JetBrains.Metadata.Utils;
using JetBrains.ProjectModel;
using JetBrains.Rd;
using JetBrains.Util;
using Microsoft.CodeAnalysis;
namespace JetBrains.ForTea.RiderPlugin.TemplateProcessing.CodeGeneration.Reference.Impl
{
[SolutionComponent(Instantiation.DemandAnyThreadSafe)]
public sealed class T4LowLevelReferenceExtractionManager : IT4LowLevelReferenceExtractionManager
{
[NotNull] private Dictionary<string, WeakReference<MetadataReference>> MetadataReferencesCache { get; }
[NotNull] private AssemblyInfoDatabase AssemblyInfoDatabase { get; }
[NotNull] private ISolution Solution { get; }
public T4LowLevelReferenceExtractionManager(
Lifetime lifetime,
[NotNull] AssemblyInfoDatabase assemblyInfoDatabase,
[NotNull] ISolution solution
)
{
AssemblyInfoDatabase = assemblyInfoDatabase;
Solution = solution;
MetadataReferencesCache = new Dictionary<string, WeakReference<MetadataReference>>();
}
// TODO: is this necessary?
[NotNull]
private static IAssemblyResolver ProtocolAssemblyResolver { get; } = new AssemblyResolverOnFolders(
typeof(Lifetime).Assembly.GetPath().ToVirtualFileSystemPath().Parent.Parent, // JetBrains.Lifetimes on .net core
typeof(IProtocol).Assembly.GetPath().ToVirtualFileSystemPath().Parent
.Parent, // JetBrains.RdFramework on .net core
typeof(Lifetime).Assembly.GetPath().ToVirtualFileSystemPath().Parent, // JetBrains.Lifetimes
typeof(IProtocol).Assembly.GetPath().ToVirtualFileSystemPath().Parent // JetBrains.RdFramework
);
public IEnumerable<T4AssemblyReferenceInfo> ResolveTransitiveDependencies(
IList<VirtualFileSystemPath> directDependencies,
IModuleReferenceResolveContext resolveContext
)
{
var result = new List<T4AssemblyReferenceInfo>();
ResolveTransitiveDependencies(directDependencies.SelectNotNull(Resolve), resolveContext, result);
return result;
}
private T4AssemblyReferenceInfo? Resolve([NotNull] VirtualFileSystemPath path)
{
var info = AssemblyInfoDatabase.GetAssemblyName(path);
if (info == null) return null;
return new T4AssemblyReferenceInfo(info.FullName, path);
}
public MetadataReference ResolveMetadata(Lifetime lifetime, VirtualFileSystemPath filePath)
{
var path = filePath.ToNativeFileSystemPath().FullPath;
if (MetadataReferencesCache.TryGetValue(path, out var weakRef) &&
weakRef.TryGetTarget(out var cached))
return cached;
var reference = MetadataReference.CreateFromFile(path,
new MetadataReferenceProperties(kind: MetadataImageKind.Assembly));
MetadataReferencesCache[path] = new WeakReference<MetadataReference>(reference);
return reference;
}
private void ResolveTransitiveDependencies(
[NotNull] IEnumerable<T4AssemblyReferenceInfo> directDependencies,
[NotNull] IModuleReferenceResolveContext resolveContext,
[NotNull] IList<T4AssemblyReferenceInfo> destination
)
{
foreach (var directDependency in directDependencies)
{
if (destination.Any(it => it.FullName == directDependency.FullName)) continue;
destination.Add(directDependency);
var indirectDependencies = AssemblyInfoDatabase
.GetReferencedAssemblyNames(new AssemblyLocation(directDependency.Location))
.SelectNotNull<AssemblyNameInfo, T4AssemblyReferenceInfo>(
assemblyNameInfo =>
{
var resolver = BuildResolver(directDependency);
resolver.ResolveAssembly(assemblyNameInfo, out var path, resolveContext);
if (path == null)
{
var assemblyFromSolution = Solution.GetAllAssemblies()
.FirstOrDefault(assembly => assembly.AssemblyName == assemblyNameInfo);
if (assemblyFromSolution == null) return null;
return new T4AssemblyReferenceInfo(
assemblyFromSolution.FullAssemblyName,
assemblyFromSolution.Location.AssemblyPhysicalPath.NotNull()
);
}
return new T4AssemblyReferenceInfo(assemblyNameInfo.FullName, path.AssemblyPhysicalPath.NotNull());
}
);
ResolveTransitiveDependencies(indirectDependencies, resolveContext, destination);
}
}
[NotNull]
private static IAssemblyResolver BuildResolver(T4AssemblyReferenceInfo directDependency) =>
new CombiningAssemblyResolver(
new AssemblyResolverOnFolders(directDependency.Location.Parent),
ProtocolAssemblyResolver
);
}
}