Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Runtime.InteropServices;
using JetBrains.Core;
using JetBrains.Core;
using JetBrains.Rd.Tasks;
using JetBrains.Rider.FSharp.TypeProviders.Protocol.Server;
using JetBrains.Util;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
<Compile Include="src\Shim\FileSystem\FSharpSourceCache.fs" />
<Compile Include="src\Shim\FileSystem\AssemblyInfoShim.fs" />
<Compile Include="src\Shim\AssemblyReader\AssemblyReaderShim.fs" />
<Compile Include="src\Shim\TypeProviders\TypeProvidersManager.fs" />
<Compile Include="src\Shim\TypeProviders\ExtensionTypingProviderShim.fs" />
<Compile Include="src\Shim\TypeProviders\TypeProvidersClient.fs" />
<Compile Include="src\Shim\TypeProviders\TypeProvidersShim.fs" />
<Compile Include="src\Shim\TypeProviders\ZoneMarker.fs" />
<Compile Include="src\Checker\ScriptFcsProjectProvider.fs" />
<Compile Include="src\Checker\FcsProjectBuilder.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager:
/// which makes FCS cached resolve results stale
[<SolutionComponent(Instantiation.DemandAnyThreadUnsafe)>]
type OutputAssemblyChangeInvalidator(lifetime: Lifetime, outputAssemblies: OutputAssemblies, daemon: IDaemon,
psiFiles: IPsiFiles, fcsProjectProvider: IFcsProjectProvider, typeProvidersShim: IProxyExtensionTypingProvider,
psiFiles: IPsiFiles, fcsProjectProvider: IFcsProjectProvider, typeProvidersShim: ITypeProvidersShim,
fcsAssemblyReaderShim: ILazy<IFcsAssemblyReaderShim>) =

interface ISolutionLoadTasksStartPsiListener2 with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ open System.Collections.Generic
open FSharp.Compiler.TypeProviders
open FSharp.Compiler.Text
open FSharp.Core.CompilerServices
open JetBrains.Core
open JetBrains.Diagnostics
open JetBrains.Lifetimes
open JetBrains.ProjectModel
open JetBrains.ProjectModel.Build
open JetBrains.ReSharper.Plugins.FSharp.Checker
Expand All @@ -21,31 +23,31 @@ open JetBrains.Rider.FSharp.TypeProviders.Protocol.Client
open JetBrains.Util.Concurrency

type internal TypeProvidersCache() =
let typeProvidersPerAssembly = ConcurrentDictionary<string, ConcurrentDictionary<int, IProxyTypeProvider>>()
let typeProvidersPerScriptOrProjectOutputPath = ConcurrentDictionary<string, ConcurrentDictionary<int, IProxyTypeProvider>>()
let proxyTypeProvidersPerId = ConcurrentDictionary<int, IProxyTypeProvider>()

let rec addTypeProvider (projectAssembly: string) (tp: IProxyTypeProvider) =
match typeProvidersPerAssembly.TryGetValue(projectAssembly) with
let rec addTypeProvider (scriptOrProjectOutputPath: string) (tp: IProxyTypeProvider) =
match typeProvidersPerScriptOrProjectOutputPath.TryGetValue(scriptOrProjectOutputPath) with
| true, assemblyCache ->
match assemblyCache.TryGetValue(tp.EntityId) with
| true, _ -> ()
| false, _ ->
assemblyCache.TryAdd(tp.EntityId, tp) |> ignore
proxyTypeProvidersPerId.TryAdd(tp.EntityId, tp) |> ignore
tp.Disposed.Add(fun _ -> removeTypeProvider projectAssembly tp.EntityId)
tp.Disposed.Add(fun _ -> removeTypeProvider scriptOrProjectOutputPath tp.EntityId)
| false, _ ->
typeProvidersPerAssembly.TryAdd(projectAssembly, ConcurrentDictionary()) |> ignore
addTypeProvider projectAssembly tp
typeProvidersPerScriptOrProjectOutputPath.TryAdd(scriptOrProjectOutputPath, ConcurrentDictionary()) |> ignore
addTypeProvider scriptOrProjectOutputPath tp

and removeTypeProvider projectAssembly tpId =
typeProvidersPerAssembly[projectAssembly].TryRemove(tpId) |> ignore
and removeTypeProvider scriptOrProjectOutputPath tpId =
typeProvidersPerScriptOrProjectOutputPath[scriptOrProjectOutputPath].TryRemove(tpId) |> ignore
proxyTypeProvidersPerId.TryRemove(tpId) |> ignore

if typeProvidersPerAssembly[projectAssembly].Count = 0 then
typeProvidersPerAssembly.TryRemove(projectAssembly) |> ignore
if typeProvidersPerScriptOrProjectOutputPath[scriptOrProjectOutputPath].Count = 0 then
typeProvidersPerScriptOrProjectOutputPath.TryRemove(scriptOrProjectOutputPath) |> ignore

member x.Add(projectAssembly, tp) =
addTypeProvider projectAssembly tp
member x.Add(scriptOrProjectOutputPath, tp) =
addTypeProvider scriptOrProjectOutputPath tp

member x.TryGet(id) = proxyTypeProvidersPerId.TryGetValue(id)

Expand All @@ -55,7 +57,7 @@ type internal TypeProvidersCache() =
| _ -> Assertion.Fail($"Cannot get type provider {id} from TypeProvidersCache"); null

member x.Get(projectOutputPath) =
match typeProvidersPerAssembly.TryGetValue(projectOutputPath) with
match typeProvidersPerScriptOrProjectOutputPath.TryGetValue(projectOutputPath) with
| true, x -> x.Values
| _ -> [||]

Expand All @@ -69,8 +71,11 @@ type internal TypeProvidersCache() =

$"Type Providers:\n{typeProviders}"

type TypeProvidersHostingScope =
| Solution
| Scripts

type IProxyTypeProvidersManager =
type ITypeProvidersClient =
abstract member GetOrCreate:
runTimeAssemblyFileName: string *
designTimeAssemblyNameString: string *
Expand All @@ -82,28 +87,20 @@ type IProxyTypeProvidersManager =
compilerToolsPath: string list *
m: range -> ITypeProvider list

abstract member Scope: TypeProvidersHostingScope
abstract member Context: TypeProvidersContext
abstract member HasGenerativeTypeProviders: project: IProject -> bool
abstract member IsActive: bool
abstract member RuntimeVersion: string
abstract member Terminate: unit -> unit
abstract member Dump: unit -> string

type TypeProvidersManager(connection: TypeProvidersConnection, fcsProjectProvider: IFcsProjectProvider,
scriptPsiModulesProvider: FSharpScriptPsiModulesProvider,
outputAssemblies: OutputAssemblies, enableGenerativeTypeProvidersInMemoryAnalysis) =
[<AbstractClass>]
type internal TypeProvidersClientBase(lifetimeDef: LifetimeDefinition, connection: TypeProvidersConnection,
enableGenerativeTypeProvidersInMemoryAnalysis) =
let protocol = connection.ProtocolModel.RdTypeProviderProcessModel
let lifetime = connection.Lifetime
let tpContext = TypeProvidersContext(connection, enableGenerativeTypeProvidersInMemoryAnalysis)
let typeProviders = TypeProvidersCache()
let lock = SpinWaitLockRef()
let projectsWithGenerativeProviders = HashSet<string>()

let addProjectWithGenerativeProvider outputPath =
let outputAssemblyPath = VirtualFileSystemPath.Parse(outputPath, InteractionContext.SolutionContext)
Assertion.Assert(not outputAssemblyPath.IsEmpty, "OutputAssemblyPath expected to be not empty")
match outputAssemblies.TryGetProjectByOutputAssemblyLocation(outputAssemblyPath) with
| null -> ()
| project ->
use lock = lock.Push()
projectsWithGenerativeProviders.Add(project.GetPersistentID()) |> ignore

let disposeTypeProviders (path: string) =
let providersToDispose = typeProviders.Get(path)
Expand All @@ -123,28 +120,21 @@ type TypeProvidersManager(connection: TypeProvidersConnection, fcsProjectProvide
| true, provider -> provider.OnInvalidate()
| _ -> ()))

fcsProjectProvider.ProjectRemoved.Advise(lifetime, fun (projectKey, fcsProject) ->
use lock = lock.Push()
let project = projectKey.Project
projectsWithGenerativeProviders.Remove(project.GetPersistentID()) |> ignore
disposeTypeProviders fcsProject.OutputPath.FullPath
)

scriptPsiModulesProvider.ModuleInvalidated.Advise(lifetime,
fun psiModule -> disposeTypeProviders psiModule.Path.FullPath)
member this.Lifetime = lifetime
member this.DisposeTypeProviders(path: string) = disposeTypeProviders path
abstract member CreateTypeProviders: RdTypeProvider[] * TypeProvidersContext * ResolutionEnvironment -> IProxyTypeProvider list
abstract member Scope: TypeProvidersHostingScope

interface IProxyTypeProvidersManager with
interface ITypeProvidersClient with
member x.GetOrCreate(runTimeAssemblyFileName: string, designTimeAssemblyNameString: string,
resolutionEnvironment: ResolutionEnvironment, isInvalidationSupported: bool, isInteractive: bool,
systemRuntimeContainsType: string -> bool, systemRuntimeAssemblyVersion: Version,
compilerToolsPath: string list, m: range) =

let envPath, projectPsiModule =
let envPath =
match resolutionEnvironment.OutputFile with
| Some file ->
// todo: module might have been changed after project changes
file, fcsProjectProvider.GetPsiModule(VirtualFileSystemPath.Parse(file, InteractionContext.SolutionContext))
| None -> m.FileName, None
| Some file -> file
| None -> m.FileName

let fakeTcImports = TcImportsHack.GetFakeTcImports(systemRuntimeContainsType)

Expand All @@ -156,25 +146,96 @@ type TypeProvidersManager(connection: TypeProvidersConnection, fcsProjectProvide
isInvalidationSupported, isInteractive, systemRuntimeAssemblyVersion.ToString(),
compilerToolsPath |> Array.ofList, fakeTcImports, envPath), RpcTimeouts.Maximal)

[ for tp in result.TypeProviders ->
let tp = new ProxyTypeProvider(tp, tpContext, Option.toObj projectPsiModule)

if not isInteractive then
tp.ContainsGenerativeTypes.Add(fun _ -> addProjectWithGenerativeProvider envPath)

[ for tp in x.CreateTypeProviders(result.TypeProviders, tpContext, resolutionEnvironment) ->
typeProviders.Add(envPath, tp)
tp :> ITypeProvider

for id in result.CachedIds ->
typeProviders.Get(id) :> ITypeProvider ])
typeProviders.Get(id) ])

typeProviderProxies

member this.Scope = this.Scope
member this.Context = tpContext
member this.IsActive = connection.IsActive
member this.Terminate() = lifetimeDef.Terminate()

member this.Dump() =
let this = this :> ITypeProvidersClient
let scope = this.Scope

if not this.IsActive then $"{scope}: Out-of-process disabled" else

let inProcessDump =
$"[{scope} - In-Process dump]:\n\n" + $"{typeProviders.Dump()}\n\n{tpContext.Dump()}"

let outOfProcessDump =
$"[{scope} - Out-Process dump]:\n\n{connection.ProtocolModel.RdTestHost.Dump.Sync(Unit.Instance)}"

$"{inProcessDump}\n\n{outOfProcessDump}"

member this.RuntimeVersion =
connection.ProtocolModel.RdTestHost.RuntimeVersion.Sync(Unit.Instance)


type internal SolutionTypeProvidersClient(lifetimeDef: LifetimeDefinition,
connection: TypeProvidersConnection,
fcsProjectProvider: IFcsProjectProvider,
outputAssemblies: OutputAssemblies,
enableGenerativeTypeProvidersInMemoryAnalysis) as this =
inherit TypeProvidersClientBase(lifetimeDef, connection, enableGenerativeTypeProvidersInMemoryAnalysis)

member this.HasGenerativeTypeProviders(project) =
let lock = SpinWaitLockRef()
let projectsWithGenerativeProviders = HashSet<string>()

do
fcsProjectProvider.ProjectRemoved.Advise(this.Lifetime, fun (projectKey, fcsProject) ->
use lock = lock.Push()
projectsWithGenerativeProviders.Contains(project.GetPersistentID())
let project = projectKey.Project
projectsWithGenerativeProviders.Remove(project.GetPersistentID()) |> ignore
this.DisposeTypeProviders(fcsProject.OutputPath.FullPath)
)

member this.Dump() =
$"{typeProviders.Dump()}\n\n{tpContext.Dump()}"
let addProjectWithGenerativeProvider outputPath =
let outputAssemblyPath = VirtualFileSystemPath.Parse(outputPath, InteractionContext.SolutionContext)
Assertion.Assert(not outputAssemblyPath.IsEmpty, "OutputAssemblyPath expected to be not empty")
match outputAssemblies.TryGetProjectByOutputAssemblyLocation(outputAssemblyPath) with
| null -> ()
| project ->
use lock = lock.Push()
projectsWithGenerativeProviders.Add(project.GetPersistentID()) |> ignore

override this.Scope = TypeProvidersHostingScope.Solution

override this.CreateTypeProviders(tps, context, resolutionEnv) =
let psiModule =
resolutionEnv.OutputFile
|> Option.bind (fun file -> fcsProjectProvider.GetPsiModule(VirtualFileSystemPath.Parse(file, InteractionContext.SolutionContext)))
|> Option.toObj

let typeProviders = [ for tp in tps -> new ProxyTypeProvider(tp, context, psiModule) :> IProxyTypeProvider ]
match resolutionEnv.OutputFile with
| None -> ()
| Some outputFile ->
for tp in typeProviders do
tp.ContainsGenerativeTypes.Add(fun _ -> addProjectWithGenerativeProvider outputFile)

typeProviders

member this.HasGenerativeTypeProviders(project: IProject) =
use lock = lock.Push()
projectsWithGenerativeProviders.Contains(project.GetPersistentID())


type internal ScriptTypeProvidersClient(lifetimeDef: LifetimeDefinition, connection: TypeProvidersConnection,
scriptPsiModulesProvider: FSharpScriptPsiModulesProvider) as this =
inherit TypeProvidersClientBase(lifetimeDef, connection, false)

do
scriptPsiModulesProvider.ModuleInvalidated.Advise(this.Lifetime, fun psiModule ->
this.DisposeTypeProviders(psiModule.Path.FullPath))

override this.Scope = TypeProvidersHostingScope.Scripts

override this.CreateTypeProviders(tps, context, _) =
[ for tp in tps -> new ProxyTypeProvider(tp, context, null) ]
Loading
Loading