Skip to content

Commit d535a02

Browse files
committed
add time stamp cache
1 parent 438d7c3 commit d535a02

File tree

8 files changed

+149
-107
lines changed

8 files changed

+149
-107
lines changed

src/absil/illib.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,6 @@ module Shim =
968968
abstract AssemblyLoadFrom: fileName:string -> System.Reflection.Assembly
969969
abstract AssemblyLoad: assemblyName:System.Reflection.AssemblyName -> System.Reflection.Assembly
970970

971-
972971
type DefaultFileSystem() =
973972
interface IFileSystem with
974973
member __.AssemblyLoadFrom(fileName:string) =

src/fsharp/CompileOps.fs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,13 +1749,21 @@ type IRawFSharpAssemblyData =
17491749
abstract HasAnyFSharpSignatureDataAttribute : ILGlobals -> bool
17501750
abstract HasMatchingFSharpSignatureDataAttribute : ILGlobals -> bool
17511751

1752-
type IProjectReference =
1752+
type TimeStampCache() =
1753+
let now = DateTime.Now
1754+
let files = Dictionary<string,DateTime>()
1755+
let projects = Dictionary<IProjectReference,DateTime>(HashIdentity.Reference)
1756+
member x.Now = now
1757+
member x.Files = files
1758+
member x.Projects = projects
1759+
1760+
and IProjectReference =
17531761
/// The name of the assembly file generated by the project
17541762
abstract FileName : string
17551763
/// Evaluate raw contents of the assembly file generated by the project
17561764
abstract EvaluateRawContents : unit -> IRawFSharpAssemblyData option
17571765
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project
1758-
abstract GetLogicalTimeStamp : unit -> System.DateTime option
1766+
abstract GetLogicalTimeStamp : TimeStampCache -> DateTime option
17591767

17601768
type AssemblyReference =
17611769
| AssemblyReference of range * string * IProjectReference option

src/fsharp/CompileOps.fsi

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
/// Coordinating compiler operations - configuration, loading initial context, reporting errors etc.
44
module internal Microsoft.FSharp.Compiler.CompileOps
55

6+
open System
67
open System.Text
8+
open System.Collections.Generic
79
open Internal.Utilities
810
open Microsoft.FSharp.Compiler.AbstractIL
911
open Microsoft.FSharp.Compiler.AbstractIL.IL
@@ -164,13 +166,20 @@ type IRawFSharpAssemblyData =
164166
abstract ILAssemblyRefs : ILAssemblyRef list
165167
abstract ShortAssemblyName : string
166168

167-
type IProjectReference =
169+
170+
type TimeStampCache =
171+
new : unit -> TimeStampCache
172+
member Now: DateTime
173+
member Files: Dictionary<string,DateTime>
174+
member Projects:Dictionary<IProjectReference,DateTime>
175+
176+
and IProjectReference =
168177
/// The name of the assembly file generated by the project
169178
abstract FileName : string
170179
/// Evaluate raw contents of the assembly file generated by the project
171180
abstract EvaluateRawContents : unit -> IRawFSharpAssemblyData option
172181
/// Get the logical timestamp that would be the timestamp of the assembly file generated by the project
173-
abstract GetLogicalTimeStamp : unit -> System.DateTime option
182+
abstract GetLogicalTimeStamp : TimeStampCache -> DateTime option
174183

175184
type AssemblyReference =
176185
| AssemblyReference of range * string * IProjectReference option

src/fsharp/vs/IncrementalBuild.fs

Lines changed: 84 additions & 83 deletions
Large diffs are not rendered by default.

src/fsharp/vs/IncrementalBuild.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ type internal IncrementalBuilder =
118118
member ThereAreLiveTypeProviders : bool
119119
#endif
120120
/// Perform one step in the F# build. Return true if the background work is finished.
121-
member Step : unit -> bool
121+
member Step : TimeStampCache -> bool
122122

123123
/// Get the preceding typecheck state of a slot, without checking if it is up-to-date w.r.t.
124124
/// the timestamps on files and referenced DLLs prior to this one. Return None if the result is not available.
@@ -155,7 +155,7 @@ type internal IncrementalBuilder =
155155
member GetCheckResultsAndImplementationsForProject : unit -> PartialCheckResults * IL.ILAssemblyRef * IRawFSharpAssemblyData option * Tast.TypedAssembly option
156156

157157
/// Get the logical time stamp that is associated with the output of the project if it were gully built immediately
158-
member GetLogicalTimeStampForProject: unit -> DateTime
158+
member GetLogicalTimeStampForProject: cache: TimeStampCache -> DateTime
159159

160160
/// Await the untyped parse results for a particular slot in the vector of parse results.
161161
///

src/fsharp/vs/ServiceUntypedParse.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type FSharpParseFileResults =
3131
/// Return the inner-most range associated with a possible breakpoint location
3232
member ValidateBreakpointLocation : pos:pos -> range option
3333

34-
/// When these files change then the build is invalid
34+
[<System.Obsolete("This property is now on FSharpCheckFileResults and FSharpCheckProjectResults. It indicates the set of file dependencies for checking a file or project.")>]
3535
member DependencyFiles : string list
3636

3737
/// Get the errors and warnings for the parse

src/fsharp/vs/service.fs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,7 +1878,7 @@ type FSharpProjectContext(thisCcu: CcuThunk, assemblies: FSharpAssembly list, ad
18781878

18791879
[<Sealed>]
18801880
// 'details' is an option because the creation of the tcGlobals etc. for the project may have failed.
1881-
type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedAssembly option) option, reactorOps: IReactorOperations) =
1881+
type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedAssembly option * string list) option, reactorOps: IReactorOperations) =
18821882

18831883
let getDetails() =
18841884
match details with
@@ -1890,12 +1890,12 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[],
18901890
member info.HasCriticalErrors = details.IsNone
18911891

18921892
member info.AssemblySignature =
1893-
let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr) = getDetails()
1893+
let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
18941894
FSharpAssemblySignature(tcGlobals, thisCcu, tcImports, topAttribs, ccuSig)
18951895

18961896
member info.AssemblyContents =
18971897
if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to tru on the FSharpChecker in order to access the checked contents of assemblies"
1898-
let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr) = getDetails()
1898+
let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles) = getDetails()
18991899
let mimpls =
19001900
match tcAssemblyExpr with
19011901
| None -> []
@@ -1904,7 +1904,7 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[],
19041904

19051905
// Not, this does not have to be a SyncOp, it can be called from any thread
19061906
member info.GetUsesOfSymbol(symbol:FSharpSymbol) =
1907-
let (tcGlobals, _tcImports, _thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr) = getDetails()
1907+
let (tcGlobals, _tcImports, _thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
19081908
// This probably doesn't need to be run on the reactor since all data touched by GetUsesOfSymbol is immutable.
19091909
reactorOps.EnqueueAndAwaitOpAsync("GetUsesOfSymbol", fun _ct ->
19101910
[| for r in tcSymbolUses do yield! r.GetUsesOfSymbol(symbol.Item) |]
@@ -1914,7 +1914,7 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[],
19141914

19151915
// Not, this does not have to be a SyncOp, it can be called from any thread
19161916
member info.GetAllUsesOfAllSymbols() =
1917-
let (tcGlobals, tcImports, thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr) = getDetails()
1917+
let (tcGlobals, tcImports, thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
19181918
// This probably doesn't need to be run on the reactor since all data touched by GetAllUsesOfSymbols is immutable.
19191919
reactorOps.EnqueueAndAwaitOpAsync("GetAllUsesOfAllSymbols", fun _ct ->
19201920
[| for r in tcSymbolUses do
@@ -1923,18 +1923,22 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[],
19231923
yield FSharpSymbolUse(tcGlobals, denv, symbol, itemOcc, m) |])
19241924

19251925
member info.ProjectContext =
1926-
let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, ad, _tcAssemblyExpr) = getDetails()
1926+
let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
19271927
let assemblies =
19281928
[ for x in tcImports.GetImportedAssemblies() do
19291929
yield FSharpAssembly(tcGlobals, tcImports, x.FSharpViewOfMetadata) ]
19301930
FSharpProjectContext(thisCcu, assemblies, ad)
19311931

19321932
member info.RawFSharpAssemblyData =
1933-
let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr) = getDetails()
1933+
let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
19341934
tcAssemblyData
19351935

1936+
member info.DependencyFiles =
1937+
let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, dependencyFiles) = getDetails()
1938+
dependencyFiles
1939+
19361940
member info.AssemblyFullName =
1937-
let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, ilAssemRef, _ad, _tcAssemblyExpr) = getDetails()
1941+
let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails()
19381942
ilAssemRef.QualifiedName
19391943

19401944
[<Sealed>]
@@ -2084,6 +2088,12 @@ type FSharpCheckFileResults(errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo
20842088
// This operation is not asynchronous - GetReferencedAssemblies can be run on the calling thread
20852089
FSharpProjectContext(scope.ThisCcu, scope.GetReferencedAssemblies(), scope.AccessRights))
20862090

2091+
member info.DependencyFiles =
2092+
threadSafeOp
2093+
(fun () -> [])
2094+
(fun (_scope, builder, _reactor) -> match builder with None -> [] | Some b -> b.Dependencies)
2095+
2096+
20872097
member info.GetAllUsesOfAllSymbolsInFile() =
20882098
reactorOp "GetAllUsesOfAllSymbolsInFile" [| |] (fun scope ->
20892099
[| for (item,itemOcc,denv,m) in scope.ScopeSymbolUses.GetAllUsesOfSymbols() do
@@ -2226,8 +2236,8 @@ type BackgroundCompiler(projectCacheSize, keepAssemblyContents, keepAllBackgroun
22262236
member x.EvaluateRawContents() =
22272237
let r = self.ParseAndCheckProjectImpl(opts)
22282238
r.RawFSharpAssemblyData
2229-
member x.GetLogicalTimeStamp() =
2230-
self.GetLogicalTimeStampForProject(opts)
2239+
member x.GetLogicalTimeStamp cache =
2240+
self.GetLogicalTimeStampForProject(cache,opts)
22312241
member x.FileName = nm } ]
22322242

22332243
let builderOpt, errorsAndWarnings =
@@ -2541,14 +2551,14 @@ type BackgroundCompiler(projectCacheSize, keepAssemblyContents, keepAllBackgroun
25412551
| Some builder ->
25422552
let (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject()
25432553
let errors = [| yield! creationErrors; yield! Parser.CreateErrorInfos (tcProj.TcConfig, true, Microsoft.FSharp.Compiler.TcGlobals.DummyFileNameForRangesWithoutASpecificLocation, tcProj.Errors) |]
2544-
FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt), reactorOps)
2554+
FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, builder.Dependencies), reactorOps)
25452555

25462556
/// Get the timestamp that would be on the output if fully built immediately
2547-
member private bc.GetLogicalTimeStampForProject(options) =
2557+
member private bc.GetLogicalTimeStampForProject(cache, options) =
25482558
let builderOpt,_creationErrors,_ = getOrCreateBuilder options
25492559
match builderOpt with
25502560
| None -> None
2551-
| Some builder -> Some (builder.GetLogicalTimeStampForProject())
2561+
| Some builder -> Some (builder.GetLogicalTimeStampForProject cache)
25522562

25532563
/// Parse and typecheck the whole project.
25542564
member bc.ParseAndCheckProject(options) =
@@ -2612,11 +2622,12 @@ type BackgroundCompiler(projectCacheSize, keepAssemblyContents, keepAllBackgroun
26122622
#endif
26132623

26142624
member bc.CheckProjectInBackground(options) =
2625+
let cache = TimeStampCache() // Only one TimeStampCache is used for the duration of each background project check
26152626
reactor.SetBackgroundOp(Some(fun () ->
26162627
let builderOpt,_,_ = getOrCreateBuilder options
26172628
match builderOpt with
26182629
| None -> false
2619-
| Some builder -> builder.Step()))
2630+
| Some builder -> builder.Step(cache)))
26202631

26212632
member bc.StopBackgroundCompile() =
26222633
reactor.SetBackgroundOp(None)
@@ -2758,6 +2769,11 @@ type FSharpChecker(projectCacheSize, keepAssemblyContents, keepAllBackgroundReso
27582769
member ic.InvalidateConfiguration(options: FSharpProjectOptions) =
27592770
backgroundCompiler.InvalidateConfiguration options
27602771

2772+
/// This function is called when the configuration is known to have changed for reasons not encoded in the ProjectOptions.
2773+
/// For example, dependent references may have been deleted or created.
2774+
member ic.NotifyDependencyChanged(options: FSharpProjectOptions) =
2775+
backgroundCompiler.InvalidateConfiguration options
2776+
27612777
/// This function is called when a project has been cleaned, and thus type providers should be refreshed.
27622778
member ic.NotifyProjectCleaned(options: FSharpProjectOptions) =
27632779
backgroundCompiler.NotifyProjectCleaned options
@@ -2896,7 +2912,7 @@ type FsiInteractiveChecker(reactorOps: IReactorOperations, tcConfig, tcGlobals,
28962912
| Parser.TypeCheckAborted.No scope ->
28972913
let errors = [| yield! parseErrors; yield! tcErrors |]
28982914
let typeCheckResults = FSharpCheckFileResults (errors,Some scope, None, reactorOps)
2899-
let projectResults = FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None), reactorOps)
2915+
let projectResults = FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps)
29002916
parseResults, typeCheckResults, projectResults
29012917
| _ ->
29022918
failwith "unexpected aborted"

src/fsharp/vs/service.fsi

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ type FSharpProjectContext =
100100
/// Get the accessibility rights for this project context w.r.t. InternalsVisibleTo attributes granting access to other assemblies
101101
member AccessibilityRights : FSharpAccessibilityRights
102102

103+
103104
/// Represents the use of an F# symbol from F# source code
104105
[<Sealed>]
105106
type FSharpSymbolUse =
@@ -157,6 +158,11 @@ type FSharpCheckFileResults =
157158
/// an unrecoverable error in earlier checking/parsing/resolution steps.
158159
member HasFullTypeCheckInfo: bool
159160

161+
/// Indicates the set of files which must be watched to accurately track changes that affect these results,
162+
/// Clients interested in reacting to updates to these files should watch these files and take actions as described
163+
/// in the documentation for compiler service.
164+
member DependencyFiles : string list
165+
160166
/// <summary>Get the items for a declaration list</summary>
161167
///
162168
/// <param name="ParsedFileResultsOpt">
@@ -272,7 +278,6 @@ type FSharpCheckFileResults =
272278
/// Get the textual usages that resolved to the given symbol throughout the file
273279
member GetUsesOfSymbolInFile : symbol:FSharpSymbol -> Async<FSharpSymbolUse[]>
274280

275-
276281
[<System.Obsolete("Please change to use GetSymbolUseAtLocation(...).Symbol")>]
277282
member GetSymbolAtLocationAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list -> Async<FSharpSymbol option>
278283

@@ -332,6 +337,10 @@ type FSharpCheckProjectResults =
332337
/// Indicates if critical errors existed in the project options
333338
member HasCriticalErrors : bool
334339

340+
/// Indicates the set of files which must be watched to accurately track changes that affect these results,
341+
/// Clients interested in reacting to updates to these files should watch these files and take actions as described
342+
/// in the documentation for compiler service.
343+
member DependencyFiles : string list
335344

336345
/// <summary>Unused in this API</summary>
337346
type UnresolvedReferencesSet

0 commit comments

Comments
 (0)