PageRenderTime 34ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/2.0/Aug2011.1/src/fsharp/vs/IncrementalBuild.fs

#
F# | 1567 lines | 1234 code | 188 blank | 145 comment | 180 complexity | 55f7cb04dfb2ee5c0c5f45884cb77d80 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
  1. namespace Microsoft.FSharp.Compiler
  2. #nowarn "57"
  3. open Internal.Utilities.Debug
  4. open System
  5. open System.IO
  6. open System.Reflection
  7. open System.Diagnostics
  8. open System.Collections.Generic
  9. open System
  10. open Microsoft.FSharp.Compiler.Tastops
  11. open Microsoft.FSharp.Compiler.Lib
  12. open Microsoft.FSharp.Compiler.AbstractIL
  13. open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
  14. module internal IncrementalBuild =
  15. /// A particular node in the Expr language. Use an int for keys instead of the entire Expr to avoid extra hashing.
  16. type Id =
  17. | Id of int
  18. static member toInt (Id(id)) = id
  19. override id.ToString() = match id with Id(n)->sprintf "Id(%d)" n
  20. [<NoEquality; NoComparison>]
  21. type ScalarExpr =
  22. | ScalarInput of Id * (*name*)string
  23. | ScalarDemultiplex of Id * (*name*)string * (*input*) VectorExpr * (*task function*) (obj array -> obj)
  24. | ScalarMap of Id * (*name*) string * (*input*) ScalarExpr * (*task function*) (obj->obj)
  25. /// Get the Id for the given ScalarExpr.
  26. static member GetId = function
  27. | ScalarInput(id,_)->id
  28. | ScalarDemultiplex(id,_,_,_)->id
  29. | ScalarMap(id,_,_,_)->id
  30. /// Get the Name for the givenScalarExpr.
  31. static member GetName = function
  32. | ScalarInput(_,n)->n
  33. | ScalarDemultiplex(_,n,_,_)->n
  34. | ScalarMap(_,n,_,_)->n
  35. override ve.ToString() =
  36. match ve with
  37. | ScalarInput(Id(id),name)->sprintf "InputScalar(%d,%s)" id name
  38. | ScalarDemultiplex(Id(id),name,_,_)->sprintf "ScalarDemultiplex(%d,%s)" id name
  39. | ScalarMap(Id(id),name,_,_)->sprintf "ScalarMap(%d,%s)" id name
  40. and VectorExpr =
  41. | VectorInput of Id * (*name*)string * Type
  42. | VectorScanLeft of Id * (*name*)string * (*accumulator*)ScalarExpr * (*input vector*)VectorExpr * (*task function*)(obj->obj->Eventually<obj>)
  43. | VectorMap of Id * (*taskname*)string * (*input*)VectorExpr * (*task function*)(obj->obj)
  44. | VectorStamp of Id * (*taskname*)string * (*input*)VectorExpr * (*task function*)(obj->DateTime)
  45. | VectorMultiplex of Id * (*taskname*)string * (*input*)ScalarExpr * (*task function*)(obj->obj array)
  46. /// Get the Id for the given VectorExpr.
  47. static member GetId = function
  48. | VectorInput(id,_,_)->id
  49. | VectorScanLeft(id,_,_,_,_)->id
  50. | VectorMap(id,_,_,_)->id
  51. | VectorStamp(id,_,_,_)->id
  52. | VectorMultiplex(id,_,_,_)->id
  53. /// Get the Name for the given VectorExpr.
  54. static member GetName = function
  55. | VectorInput(_,n,_)->n
  56. | VectorScanLeft(_,n,_,_,_)->n
  57. | VectorMap(_,n,_,_)->n
  58. | VectorStamp(_,n,_,_)->n
  59. | VectorMultiplex(_,n,_,_)->n
  60. override ve.ToString() =
  61. match ve with
  62. | VectorInput(Id(id),name,_)->sprintf "VectorInput(%d,%s)" id name
  63. | VectorScanLeft(Id(id),name,_,_,_)->sprintf "VectorScanLeft(%d,%s)" id name
  64. | VectorMap(Id(id),name,_,_)->sprintf "VectorMap(%d,%s)" id name
  65. | VectorStamp(Id(id),name,_,_)->sprintf "VectorStamp(%d,%s)" id name
  66. | VectorMultiplex(Id(id),name,_,_)->sprintf "VectorMultiplex(%d,%s)" id name
  67. [<NoEquality; NoComparison>]
  68. type Expr =
  69. | ScalarExpr of ScalarExpr
  70. | VectorExpr of VectorExpr
  71. /// Get the Id for the given Expr.
  72. static member GetId = function
  73. | ScalarExpr(se)->ScalarExpr.GetId(se)
  74. | VectorExpr(ve)->VectorExpr.GetId(ve)
  75. /// Get the Name for the given Expr.
  76. static member GetName= function
  77. | ScalarExpr(se)->ScalarExpr.GetName(se)
  78. | VectorExpr(ve)->VectorExpr.GetName(ve)
  79. override e.ToString() =
  80. match e with
  81. | ScalarExpr _ -> sprintf "ScalarExpr(se)"
  82. | VectorExpr _ -> sprintf "VectorExpr(ve)"
  83. // Ids of exprs
  84. let nextid = ref 999 // Number ids starting with 1000 to discern them
  85. let NextId() =
  86. nextid:=!nextid+1
  87. Id(!nextid)
  88. type IScalar =
  89. abstract GetScalarExpr : unit -> ScalarExpr
  90. type IVector =
  91. abstract GetVectorExpr : unit-> VectorExpr
  92. type Scalar<'T> = interface
  93. end
  94. type Vector<'T> = interface
  95. end
  96. /// The outputs of a build
  97. [<NoEquality; NoComparison>]
  98. type NamedOutput =
  99. | NamedVectorOutput of string * IVector
  100. | NamedScalarOutput of string * IScalar
  101. /// Visit each task and call op with the given accumulator.
  102. let ForeachExpr(rules, op, acc)=
  103. let rec VisitVector (ve:VectorExpr) acc =
  104. match ve with
  105. | VectorInput(_)->op (VectorExpr ve) acc
  106. | VectorScanLeft(_,_,a,i,_)->op (VectorExpr ve) (VisitVector i (VisitScalar a acc))
  107. | VectorMap(_,_,i,_)
  108. | VectorStamp(_,_,i,_)->op (VectorExpr ve) (VisitVector i acc)
  109. | VectorMultiplex(_,_,i,_)->op (VectorExpr ve) (VisitScalar i acc)
  110. and VisitScalar (se:ScalarExpr) acc =
  111. match se with
  112. | ScalarInput(_)->op (ScalarExpr se) acc
  113. | ScalarDemultiplex(_,_,i,_)->op (ScalarExpr se) (VisitVector i acc)
  114. | ScalarMap(_,_,i,_)->op (ScalarExpr se) (VisitScalar i acc)
  115. let rec Visit (expr:Expr) acc =
  116. match expr with
  117. | ScalarExpr(se)->VisitScalar se acc
  118. | VectorExpr(ve)->VisitVector ve acc
  119. List.foldBack Visit (rules |> List.map(snd)) acc
  120. /// Convert from interfaces into discriminated union.
  121. let ToBuild (names:NamedOutput list) : (string * Expr) list =
  122. // Create the rules.
  123. let CreateRules() = names |> List.map(function NamedVectorOutput(n,v) -> n,VectorExpr(v.GetVectorExpr())
  124. | NamedScalarOutput(n,s) -> n,ScalarExpr(s.GetScalarExpr()))
  125. // Ensure that all names are unique.
  126. let EnsureUniqueNames (expr:Expr) (acc:Map<string,Id>) =
  127. let AddUniqueIdToNameMapping(id,name)=
  128. match acc.TryFind name with
  129. | Some(priorId)->
  130. if id<>priorId then failwith (sprintf "Two build expressions had the same name: %s" name)
  131. else acc
  132. | None-> Map.add name id acc
  133. let id = Expr.GetId(expr)
  134. let name = Expr.GetName(expr)
  135. AddUniqueIdToNameMapping(id,name)
  136. // Validate the rule tree
  137. let ValidateRules(rules:(string*Expr) list) =
  138. ForeachExpr(rules,EnsureUniqueNames,Map.empty) |> ignore
  139. // Convert and validate
  140. let rules = CreateRules()
  141. ValidateRules(rules)
  142. rules
  143. /// These describe the input conditions for a result. If conditions change then the result is invalid.
  144. type InputSignature =
  145. | SingleMappedVectorInput of InputSignature array
  146. | EmptyTimeStampedInput of DateTime
  147. | BoundInputScalar // An external input into the build
  148. | BoundInputVector // An external input into the build
  149. | IndexedValueElement of DateTime
  150. | UnevaluatedInput
  151. /// Return true if the result is fully evaluated
  152. member is.IsEvaluated() =
  153. let rec IsEvaluated(is) =
  154. match is with
  155. | UnevaluatedInput -> false
  156. | SingleMappedVectorInput iss -> iss |> Array.forall IsEvaluated
  157. | _ -> true
  158. IsEvaluated(is)
  159. override is.ToString() = sprintf "%A" is
  160. /// A slot for holding a single result.
  161. type Result =
  162. | NotAvailable
  163. | InProgress of (unit -> Eventually<obj>) * DateTime
  164. | Available of obj * DateTime * InputSignature
  165. /// Get the available result. Throw an exception if not available.
  166. static member GetAvailable = function Available(o,_,_)->o | _->failwith "No available result"
  167. /// Get the time stamp if available. Otheriwse MaxValue.
  168. static member Timestamp = function Available(_,ts,_)->ts | InProgress(_,ts) -> ts | _-> DateTime.MaxValue
  169. /// Get the time stamp if available. Otheriwse MaxValue.
  170. static member InputSignature = function Available(_,_,signature)->signature | _-> UnevaluatedInput
  171. member x.ResultIsInProgress = match x with | InProgress _ -> true | _ -> false
  172. member x.GetInProgressContinuation() = match x with | InProgress (f,_) -> f() | _ -> failwith "not in progress"
  173. member x.TryGetAvailable() = match x with | InProgress _ | NotAvailable -> None | Available(obj,dt,i) -> Some(obj,dt,i)
  174. override r.ToString() =
  175. match r with
  176. | NotAvailable -> "NotAvailable"
  177. | InProgress _ -> "InProgress"
  178. | Available(o, ts, _) -> sprintf "Available('%s' as of %A)" (o.ToString()) ts
  179. /// An immutable sparse vector of results.
  180. type ResultVector(size,zeroElementTimestamp,map) =
  181. let get(slot) =
  182. match Map.tryFind slot map with
  183. | Some(result)->result
  184. | None->NotAvailable
  185. let asList = lazy List.map (fun i->i,get(i)) [0..size-1]
  186. static member OfSize(size) = ResultVector(size,DateTime.MinValue,Map.empty)
  187. member rv.Size = size
  188. member rv.Get(slot) = get(slot)
  189. member rv.Resize(newsize) =
  190. if size<>newsize then
  191. ResultVector(newsize, zeroElementTimestamp, map |> Map.filter(fun s _ -> s < newsize))
  192. else rv
  193. member rv.Set(slot,value) =
  194. #if DEBUG
  195. if slot<0 then failwith "ResultVector slot less than zero"
  196. if slot>=size then failwith "ResultVector slot too big"
  197. #endif
  198. ResultVector(size, zeroElementTimestamp, Map.add slot value map)
  199. member rv.MaxTimestamp() =
  200. // use t = Trace.Call("IncrementalBuildVerbose", "MaxTimestamp", fun _->sprintf "vector of size=%d" size)
  201. let Maximize (lasttimestamp:DateTime) (_,result) =
  202. let thistimestamp = Result.Timestamp result
  203. let m = max lasttimestamp thistimestamp
  204. // use t = Trace.Call("IncrementalBuildVerbose", "Maximize", fun _->sprintf "last=%s this=%s max=%s" (lasttimestamp.ToString()) (thistimestamp.ToString()) (m.ToString()))
  205. m
  206. List.fold Maximize zeroElementTimestamp (asList.Force())
  207. member rv.Signature() =
  208. let l = asList.Force()
  209. let l = l |> List.map(fun (_,result)->Result.InputSignature result)
  210. SingleMappedVectorInput (l|>List.toArray)
  211. member rv.FoldLeft f s : 'a = List.fold f s (asList.Force())
  212. override rv.ToString() = asList.ToString() // NOTE: Force()ing this inside ToString() leads to StackOverflowException and very undesirable debugging behavior for all of F#
  213. /// A result of performing build actions
  214. [<NoEquality; NoComparison>]
  215. type ResultSet =
  216. | ScalarResult of Result
  217. | VectorResult of ResultVector
  218. override rs.ToString() =
  219. match rs with
  220. | ScalarResult(sr)->sprintf "ScalarResult(%s)" (sr.ToString())
  221. | VectorResult(rs)->sprintf "VectorResult(%s)" (rs.ToString())
  222. /// Action timing
  223. module Time =
  224. #if SILVERLIGHT
  225. let Action<'T> taskname slot func : 'T = func()
  226. #else
  227. let sw = new Stopwatch()
  228. let Action<'T> taskname slot func : 'T =
  229. if Trace.ShouldLog("IncrementalBuildWorkUnits") then
  230. let slotMessage =
  231. if slot= -1 then sprintf "%s" taskname
  232. else sprintf "%s over slot %d" taskname slot
  233. // Timings and memory
  234. let maxGen = System.GC.MaxGeneration
  235. let ptime = System.Diagnostics.Process.GetCurrentProcess()
  236. let timePrev = ptime.UserProcessorTime.TotalSeconds
  237. let gcPrev = [| for i in 0 .. maxGen -> System.GC.CollectionCount(i) |]
  238. let pbPrev = ptime.PrivateMemorySize64 in
  239. // Call the function
  240. let result = func()
  241. // Report.
  242. let timeNow = ptime.UserProcessorTime.TotalSeconds
  243. let pbNow = ptime.PrivateMemorySize64
  244. let spanGC = [| for i in 0 .. maxGen -> System.GC.CollectionCount(i) - gcPrev.[i] |]
  245. Trace.PrintLine("IncrementalBuildWorkUnits", fun _ ->
  246. sprintf "%s TIME: %4.3f MEM: %3d (delta) G0: %3d G1: %2d G2: %2d"
  247. slotMessage
  248. (timeNow - timePrev)
  249. (pbNow - pbPrev)
  250. spanGC.[min 0 maxGen]
  251. spanGC.[min 1 maxGen]
  252. spanGC.[min 2 maxGen])
  253. result
  254. else func()
  255. #endif
  256. /// Result of a particular action over the bound build tree
  257. [<NoEquality; NoComparison>]
  258. type ActionResult =
  259. | IndexedResult of Id * int * (*slotcount*) int * Eventually<obj> * DateTime
  260. | ScalarValuedResult of Id * obj * DateTime * InputSignature
  261. | VectorValuedResult of Id * obj array * DateTime * InputSignature
  262. | ResizeResult of Id * (*slotcount*) int
  263. override ar.ToString() =
  264. match ar with
  265. | IndexedResult(id,slot,slotcount,_,dt)->sprintf "IndexedResult(%d,%d,%d,obj,%A)" (Id.toInt id) slot slotcount dt
  266. | ScalarValuedResult(id,_,dt,inputsig)->sprintf "ScalarValuedResult(%d,obj,%A,%A)" (Id.toInt id) dt inputsig
  267. | VectorValuedResult(id,_,dt,inputsig)->sprintf "VectorValuedResult(%d,obj array,%A,%A)" (Id.toInt id) dt inputsig
  268. | ResizeResult(id,slotcount)->sprintf "ResizeResult(%d,%d)" (Id.toInt id) slotcount
  269. /// A pending action over the bound build tree
  270. [<NoEquality; NoComparison>]
  271. type Action =
  272. | IndexedAction of Id * (*taskname*)string * int * (*slotcount*) int * DateTime * (unit->Eventually<obj>)
  273. | ScalarAction of Id * (*taskname*)string * DateTime * InputSignature * (unit->obj)
  274. | VectorAction of Id * (*taskname*)string * DateTime * InputSignature * (unit->obj array)
  275. | ResizeResultAction of Id * (*slotcount*) int
  276. /// Execute one action and return a corresponding result.
  277. static member Execute action =
  278. match action with
  279. | IndexedAction(id,taskname,slot,slotcount,timestamp,func) -> IndexedResult(id,slot,slotcount,Time.Action taskname slot func,timestamp)
  280. | ScalarAction(id,taskname,timestamp,inputsig,func) -> ScalarValuedResult(id,Time.Action taskname (-1) func,timestamp,inputsig)
  281. | VectorAction(id,taskname,timestamp,inputsig,func) -> VectorValuedResult(id,Time.Action taskname (-1) func,timestamp,inputsig)
  282. | ResizeResultAction(id,slotcount) -> ResizeResult(id,slotcount)
  283. /// String helper functions for when there's no %A
  284. type String =
  285. static member OfList2 l =
  286. " ["^String.Join(",\n ", List.toArray (l|>List.map (fun (v1,v2)->((box v1).ToString())^";"^((box v2).ToString()))))^" ]"
  287. /// A set of build rules and the corresponding, possibly partial, results from building.
  288. [<Sealed>]
  289. type Build(rules:(string * Expr) list,
  290. results:Map<Id,ResultSet>) =
  291. member bt.Rules = rules
  292. member bt.Results = results
  293. override bt.ToString() =
  294. let sb = new System.Text.StringBuilder()
  295. results |> Map.iter(fun id result->
  296. let id = Id.toInt id
  297. let s = sprintf "\n {Id=%d,ResultSet=%s}" id (result.ToString())
  298. let _ = sb.Append(s)
  299. ())
  300. sprintf "{Rules={%s}\n Results={%s}}" (String.OfList2 rules) (sb.ToString())
  301. /// Given an expression, find the expected width.
  302. let rec GetVectorWidthByExpr(bt:Build,ve:VectorExpr) =
  303. let KnownValue(ve) =
  304. match bt.Results.TryFind(VectorExpr.GetId(ve)) with
  305. | Some(resultSet) ->
  306. match resultSet with
  307. | VectorResult(rv)->Some(rv.Size)
  308. | _ -> failwith "Expected vector to have vector result."
  309. | None-> None
  310. match ve with
  311. | VectorScanLeft(_,_,_,i,_)
  312. | VectorMap(_,_,i,_)
  313. | VectorStamp(_,_,i,_)->
  314. match GetVectorWidthByExpr(bt,i) with
  315. | Some _ as r -> r
  316. | None->KnownValue(ve)
  317. | VectorInput(_,_,_)
  318. | VectorMultiplex(_,_,_,_)->KnownValue(ve)
  319. /// Given an expression name, get the corresponding expression.
  320. let GetTopLevelExprByName(bt:Build, seek:string) =
  321. bt.Rules |> List.filter(fun(name,_)->name=seek) |> List.map(fun(_,root)->root) |> List.head
  322. /// Get an expression matching the given name.
  323. let GetExprByName(bt:Build, seek:string) : Expr =
  324. let MatchName (expr:Expr) (acc:Expr option) : Expr option =
  325. let name = Expr.GetName(expr)
  326. if name = seek then Some(expr) else acc
  327. let matchOption = ForeachExpr(bt.Rules,MatchName,None)
  328. Option.get matchOption
  329. // Given an Id, find the corresponding expression.
  330. let GetExprById(bt:Build, seek:Id) : Expr=
  331. let rec VectorExprOfId(ve) =
  332. match ve with
  333. | VectorInput(id,_,_)->if seek=id then Some(VectorExpr(ve)) else None
  334. | VectorScanLeft(id,_,a,i,_)->
  335. if seek=id then Some(VectorExpr(ve)) else
  336. let result = ScalarExprOfId(a)
  337. match result with Some _ -> result | None->VectorExprOfId(i)
  338. | VectorMap(id,_,i,_)->if seek=id then Some(VectorExpr(ve)) else VectorExprOfId(i)
  339. | VectorStamp(id,_,i,_)->if seek=id then Some(VectorExpr(ve)) else VectorExprOfId(i)
  340. | VectorMultiplex(id,_,i,_)->if seek=id then Some(VectorExpr(ve)) else ScalarExprOfId(i)
  341. and ScalarExprOfId(se) =
  342. match se with
  343. | ScalarInput(id,_)->if seek=id then Some(ScalarExpr(se)) else None
  344. | ScalarDemultiplex(id,_,i,_)->if seek=id then Some(ScalarExpr(se)) else VectorExprOfId(i)
  345. | ScalarMap(id,_,i,_)->if seek=id then Some(ScalarExpr(se)) else ScalarExprOfId(i)
  346. let ExprOfId(expr:Expr) =
  347. match expr with
  348. | ScalarExpr(se)->ScalarExprOfId(se)
  349. | VectorExpr(ve)->VectorExprOfId(ve)
  350. let exprs = bt.Rules |> List.map(fun(_,root)->ExprOfId(root)) |> List.filter(Option.isSome)
  351. match exprs with
  352. | Some(expr)::_ -> expr
  353. | _ -> failwith (sprintf "GetExprById did not find an expression for Id %d" (Id.toInt seek))
  354. let GetVectorWidthById (bt:Build) seek =
  355. match GetExprById(bt,seek) with
  356. | ScalarExpr(_)->failwith "Attempt to get width of scalar."
  357. | VectorExpr(ve)->Option.get (GetVectorWidthByExpr(bt,ve))
  358. let GetScalarExprResult(bt:Build, se:ScalarExpr) =
  359. match bt.Results.TryFind(ScalarExpr.GetId(se)) with
  360. | Some(resultSet) ->
  361. match se,resultSet with
  362. | ScalarInput(_),ScalarResult(r)
  363. | ScalarMap(_),ScalarResult(r)
  364. | ScalarDemultiplex(_),ScalarResult(r)->r
  365. | se,result->failwith (sprintf "GetScalarExprResult had no match for %A,%A" se result)
  366. | None->NotAvailable
  367. let GetVectorExprResultVector(bt:Build, ve:VectorExpr) =
  368. match bt.Results.TryFind(VectorExpr.GetId(ve)) with
  369. | Some(resultSet) ->
  370. match ve,resultSet with
  371. | VectorScanLeft(_),VectorResult(rv)
  372. | VectorMap(_),VectorResult(rv)
  373. | VectorInput(_),VectorResult(rv)
  374. | VectorStamp(_),VectorResult(rv)
  375. | VectorMultiplex(_),VectorResult(rv) -> Some(rv)
  376. | ve,result->failwith (sprintf "GetVectorExprResultVector had no match for %A,%A" ve result)
  377. | None->None
  378. let GetVectorExprResult(bt:Build, ve:VectorExpr, slot) =
  379. match bt.Results.TryFind(VectorExpr.GetId(ve)) with
  380. | Some(resultSet) ->
  381. match ve,resultSet with
  382. | VectorScanLeft(_),VectorResult(rv)
  383. | VectorMap(_),VectorResult(rv)
  384. | VectorInput(_),VectorResult(rv)
  385. | VectorStamp(_),VectorResult(rv) -> rv.Get(slot)
  386. | VectorMultiplex(_),VectorResult(rv) -> rv.Get(slot)
  387. | ve,result->failwith (sprintf "GetVectorExprResult had no match for %A,%A" ve result)
  388. | None->NotAvailable
  389. /// Get the maximum build stamp for an output.
  390. let MaxTimestamp(bt:Build,id,_inputstamp) =
  391. match bt.Results.TryFind(id) with
  392. | Some(resultset) ->
  393. match resultset with
  394. | ScalarResult(rs) -> Result.Timestamp rs
  395. | VectorResult(rv) -> rv.MaxTimestamp()
  396. | None -> DateTime.MaxValue
  397. let Signature(bt:Build,id) =
  398. match bt.Results.TryFind(id) with
  399. | Some(resultset) ->
  400. match resultset with
  401. | ScalarResult(rs) -> Result.InputSignature rs
  402. | VectorResult(rv) -> rv.Signature()
  403. | None -> UnevaluatedInput
  404. /// Get all the results for the given expr.
  405. let AllResultsOfExpr extractor (bt:Build) expr =
  406. let GetAvailable (rv:ResultVector) =
  407. let Extract acc (_, result) = (extractor result)::acc
  408. List.rev (rv.FoldLeft Extract [])
  409. let GetVectorResultById id =
  410. match bt.Results.TryFind(id) with
  411. | Some(found) ->
  412. match found with
  413. | VectorResult(rv)->GetAvailable rv
  414. | _ -> failwith "wrong result type"
  415. | None -> []
  416. GetVectorResultById(VectorExpr.GetId(expr))
  417. let AvailableAllResultsOfExpr bt expr =
  418. let msg = "Expected all results to be available"
  419. AllResultsOfExpr (function Available(o,_,_) -> o | _ -> failwith msg) bt expr
  420. /// Bind a set of build rules to a set of input values.
  421. let ToBound(build:(string*Expr) list, vectorinputs, scalarinputs) =
  422. let now = DateTime.Now
  423. let rec ApplyScalarExpr(se,results) =
  424. match se with
  425. | ScalarInput(id,n) ->
  426. let matches = scalarinputs
  427. |> List.filter (fun (inputname,_)->inputname=n)
  428. |> List.map (fun (_,inputvalue:obj)-> ScalarResult(Available(inputvalue,now,BoundInputScalar)))
  429. List.foldBack (Map.add id) matches results
  430. | ScalarMap(_,_,se,_) ->ApplyScalarExpr(se,results)
  431. | ScalarDemultiplex(_,_,ve,_) ->ApplyVectorExpr(ve,results)
  432. and ApplyVectorExpr(ve,results) =
  433. match ve with
  434. | VectorInput(id,n,_) ->
  435. let matches = vectorinputs
  436. |> List.filter (fun (inputname,_,_)->inputname=n)
  437. |> List.map (fun (_,size,inputvalues:obj list)->
  438. let results = inputvalues|>List.mapi(fun i value->i,Available(value,now,BoundInputVector))
  439. VectorResult(ResultVector(size,DateTime.MinValue,results|>Map.ofList))
  440. )
  441. List.foldBack (Map.add id) matches results
  442. | VectorScanLeft(_,_,a,i,_)->ApplyVectorExpr(i,ApplyScalarExpr(a,results))
  443. | VectorMap(_,_,i,_)
  444. | VectorStamp(_,_,i,_)->ApplyVectorExpr(i,results)
  445. | VectorMultiplex(_,_,i,_)->ApplyScalarExpr(i,results)
  446. let ApplyExpr expr results =
  447. match expr with
  448. | ScalarExpr(se)->ApplyScalarExpr(se,results)
  449. | VectorExpr(ve)->ApplyVectorExpr(ve,results)
  450. // Place vector inputs into results map.
  451. let results = List.foldBack ApplyExpr (build|>List.map(snd)) (Map.empty)
  452. Build(build,results)
  453. /// Visit each executable action and call actionFunc with the given accumulator.
  454. let ForeachAction output bt (actionFunc:Action->'acc->'acc) (acc:'acc) =
  455. use t = Trace.Call("IncrementalBuildVerbose", "ForeachAction", fun _->sprintf "name=%s" output)
  456. let seen = Dictionary<_,_>()
  457. let Seen(id) =
  458. if seen.ContainsKey(id) then true
  459. else seen.[id]<-true
  460. false
  461. let HasChanged(inputtimestamp,outputtimestamp) =
  462. if inputtimestamp<>outputtimestamp then
  463. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Input timestamp is %A. Output timestamp is %A." inputtimestamp outputtimestamp)
  464. true
  465. else false
  466. let ShouldEvaluate(bt,currentsig:InputSignature,id) =
  467. let isAvailable = currentsig.IsEvaluated()
  468. if isAvailable then
  469. let priorsig = Signature(bt,id)
  470. currentsig<>priorsig
  471. else false
  472. /// Make sure the result vector saved matches the size of expr
  473. let ResizeVectorExpr(ve:VectorExpr,acc) =
  474. let id = VectorExpr.GetId(ve)
  475. match GetVectorWidthByExpr(bt,ve) with
  476. | Some(expectedWidth) ->
  477. match bt.Results.TryFind(id) with
  478. | Some(found) ->
  479. match found with
  480. | VectorResult(rv)->
  481. if rv.Size<> expectedWidth then
  482. actionFunc (ResizeResultAction(id,expectedWidth)) acc
  483. else acc
  484. | _ -> acc
  485. | None -> acc
  486. | None -> acc
  487. let rec VisitVector ve acc =
  488. if Seen(VectorExpr.GetId(ve)) then acc
  489. else
  490. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "In ForeachAction at vector expression %s" (ve.ToString()))
  491. let acc = ResizeVectorExpr(ve,acc)
  492. match ve with
  493. | VectorInput(_)->acc
  494. | VectorScanLeft(id,taskname,accumulatorExpr,inputExpr,func)->
  495. let acc =
  496. match GetVectorWidthByExpr(bt,ve) with
  497. | Some(cardinality) ->
  498. let GetInputAccumulator(slot) =
  499. if slot=0 then GetScalarExprResult(bt,accumulatorExpr)
  500. else GetVectorExprResult(bt,ve,slot-1)
  501. let Scan slot =
  502. let accumulatorResult = GetInputAccumulator(slot)
  503. let inputResult = GetVectorExprResult(bt,inputExpr,slot)
  504. match accumulatorResult,inputResult with
  505. | Available(accumulator,accumulatortimesamp,_accumulatorInputSig),Available(input,inputtimestamp,_inputSig)->
  506. let inputtimestamp = max inputtimestamp accumulatortimesamp
  507. let prevoutput = GetVectorExprResult(bt,ve,slot)
  508. let outputtimestamp = Result.Timestamp prevoutput
  509. let scanOp =
  510. if HasChanged(inputtimestamp,outputtimestamp) then
  511. Some (fun () -> func accumulator input)
  512. elif prevoutput.ResultIsInProgress then
  513. Some prevoutput.GetInProgressContinuation
  514. else
  515. // up-to-date and complete, no work required
  516. None
  517. match scanOp with
  518. | Some scanOp -> Some(actionFunc (IndexedAction(id,taskname,slot,cardinality,inputtimestamp,scanOp)) acc)
  519. | None -> None
  520. | _ -> None
  521. match ([0..cardinality-1]|>List.tryPick Scan) with Some(acc)->acc | None->acc
  522. | None -> acc
  523. // Check each slot for an action that may be performed.
  524. VisitVector inputExpr (VisitScalar accumulatorExpr acc)
  525. | VectorMap(id, taskname, inputExpr, func)->
  526. let acc =
  527. match GetVectorWidthByExpr(bt,ve) with
  528. | Some(cardinality) ->
  529. if cardinality=0 then
  530. // For vector length zero, just propagate the prior timestamp.
  531. let inputtimestamp = MaxTimestamp(bt,VectorExpr.GetId(inputExpr),DateTime.MinValue)
  532. let outputtimestamp = MaxTimestamp(bt,id,DateTime.MinValue)
  533. if HasChanged(inputtimestamp,outputtimestamp) then
  534. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Vector Map with cardinality zero setting output timestamp to %A." inputtimestamp)
  535. actionFunc (VectorAction(id,taskname,inputtimestamp,EmptyTimeStampedInput inputtimestamp, fun _ ->[||])) acc
  536. else acc
  537. else
  538. let MapResults acc slot =
  539. let inputtimestamp = Result.Timestamp (GetVectorExprResult(bt,inputExpr,slot))
  540. let outputtimestamp = Result.Timestamp (GetVectorExprResult(bt,ve,slot))
  541. if HasChanged(inputtimestamp,outputtimestamp) then
  542. let OneToOneOp() =
  543. Eventually.Done (func (Result.GetAvailable (GetVectorExprResult(bt,inputExpr,slot))))
  544. actionFunc (IndexedAction(id,taskname,slot,cardinality,inputtimestamp,OneToOneOp)) acc
  545. else acc
  546. [0..cardinality-1] |> List.fold MapResults acc
  547. | None -> acc
  548. VisitVector inputExpr acc
  549. | VectorStamp(id, taskname, inputExpr, func)->
  550. // For every result that is available, check time stamps.
  551. let acc =
  552. match GetVectorWidthByExpr(bt,ve) with
  553. | Some(cardinality) ->
  554. if cardinality=0 then
  555. // For vector length zero, just propagate the prior timestamp.
  556. let inputtimestamp = MaxTimestamp(bt,VectorExpr.GetId(inputExpr),DateTime.MinValue)
  557. let outputtimestamp = MaxTimestamp(bt,id,DateTime.MinValue)
  558. if HasChanged(inputtimestamp,outputtimestamp) then
  559. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Vector Stamp with cardinality zero setting output timestamp to %A." inputtimestamp)
  560. actionFunc (VectorAction(id,taskname,inputtimestamp,EmptyTimeStampedInput inputtimestamp,fun _ ->[||])) acc
  561. else acc
  562. else
  563. let CheckStamp acc slot =
  564. let inputresult = GetVectorExprResult(bt,inputExpr,slot)
  565. match inputresult with
  566. | Available(ires,_,_)->
  567. let oldtimestamp = Result.Timestamp (GetVectorExprResult(bt,ve,slot))
  568. let newtimestamp = func ires
  569. if newtimestamp<>oldtimestamp then
  570. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "Old timestamp was %A. New timestamp is %A." oldtimestamp newtimestamp)
  571. actionFunc (IndexedAction(id,taskname,slot,cardinality,newtimestamp, fun _ -> Eventually.Done ires)) acc
  572. else acc
  573. | _ -> acc
  574. [0..cardinality-1] |> List.fold CheckStamp acc
  575. | None -> acc
  576. VisitVector inputExpr acc
  577. | VectorMultiplex(id, taskname, inputExpr, func)->
  578. VisitScalar inputExpr
  579. (match GetScalarExprResult(bt,inputExpr) with
  580. | Available(inp,inputtimestamp,inputsig) ->
  581. let outputtimestamp = MaxTimestamp(bt,id,inputtimestamp)
  582. if HasChanged(inputtimestamp,outputtimestamp) then
  583. let MultiplexOp() = func inp
  584. actionFunc (VectorAction(id,taskname,inputtimestamp,inputsig,MultiplexOp)) acc
  585. else acc
  586. | _->acc)
  587. and VisitScalar se acc =
  588. if Seen(ScalarExpr.GetId(se)) then acc
  589. else
  590. Trace.PrintLine("IncrementalBuildVerbose", fun _ -> sprintf "In ForeachAction at scalar expression %s" (se.ToString()))
  591. match se with
  592. | ScalarInput(_)->acc
  593. | ScalarDemultiplex(id,taskname,inputExpr,func)->
  594. VisitVector inputExpr
  595. (
  596. match GetVectorExprResultVector(bt,inputExpr) with
  597. | Some(inputresult) ->
  598. let currentsig = inputresult.Signature()
  599. if ShouldEvaluate(bt,currentsig,id) then
  600. let inputtimestamp = MaxTimestamp(bt, VectorExpr.GetId(inputExpr), DateTime.MaxValue)
  601. let DemultiplexOp() =
  602. let input = AvailableAllResultsOfExpr bt inputExpr |> List.toArray
  603. func input
  604. actionFunc (ScalarAction(id,taskname,inputtimestamp,currentsig,DemultiplexOp)) acc
  605. else acc
  606. | None -> acc
  607. )
  608. | ScalarMap(id,taskname,inputExpr,func)->
  609. VisitScalar inputExpr
  610. (match GetScalarExprResult(bt,inputExpr) with
  611. | Available(inp,inputtimestamp,inputsig) ->
  612. let outputtimestamp = MaxTimestamp(bt, id, inputtimestamp)
  613. if HasChanged(inputtimestamp,outputtimestamp) then
  614. let MapOp() = func inp
  615. actionFunc (ScalarAction(id,taskname,inputtimestamp,inputsig,MapOp)) acc
  616. else acc
  617. | _->acc)
  618. let Visit expr acc =
  619. match expr with
  620. | ScalarExpr(se)->VisitScalar se acc
  621. | VectorExpr(ve)->VisitVector ve acc
  622. let filtered = bt.Rules |> List.filter (fun (s,_) -> s = output) |> List.map snd
  623. List.foldBack Visit filtered acc
  624. /// Given the result of a single action, apply that action to the Build
  625. let ApplyResult(actionResult:ActionResult,bt:Build) =
  626. use t = Trace.Call("IncrementalBuildVerbose", "ApplyResult", fun _ -> "")
  627. let result =
  628. match actionResult with
  629. | ResizeResult(id,slotcount) ->
  630. match bt.Results.TryFind(id) with
  631. | Some(resultSet) ->
  632. match resultSet with
  633. | VectorResult(rv) ->
  634. let rv = rv.Resize(slotcount)
  635. let results = Map.add id (VectorResult rv) bt.Results
  636. Build(bt.Rules,results)
  637. | _ -> failwith "Unexpected"
  638. | None -> failwith "Unexpected"
  639. | ScalarValuedResult(id,value,timestamp,inputsig)->
  640. Build(bt.Rules, Map.add id (ScalarResult(Available(value,timestamp,inputsig))) bt.Results)
  641. | VectorValuedResult(id,values,timestamp,inputsig)->
  642. let Append acc slot =
  643. Map.add slot (Available(values.[slot],timestamp,inputsig)) acc
  644. let results = [0..values.Length-1]|>List.fold Append (Map.empty)
  645. let results = VectorResult(ResultVector(values.Length,timestamp,results))
  646. let bt = Build(bt.Rules, Map.add id results bt.Results)
  647. bt
  648. | IndexedResult(id,index,slotcount,value,timestamp)->
  649. let width = (GetVectorWidthById bt id)
  650. let priorResults = bt.Results.TryFind(id)
  651. let prior =
  652. match priorResults with
  653. | Some(prior)->prior
  654. | None->VectorResult(ResultVector.OfSize width)
  655. match prior with
  656. | VectorResult(rv)->
  657. let result =
  658. match value with
  659. | Eventually.Done res ->
  660. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> "Eventually.Done...")
  661. Available(res,timestamp, IndexedValueElement timestamp)
  662. | Eventually.NotYetDone f ->
  663. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> "Eventually.NotYetDone...")
  664. InProgress (f,timestamp)
  665. let results = rv.Resize(slotcount).Set(index,result)
  666. Build(bt.Rules, Map.add id (VectorResult(results)) bt.Results)
  667. | _->failwith "Unexpected"
  668. result
  669. /// Evaluate the result of a single output
  670. let EvalLeafsFirst output bt =
  671. use t = Trace.Call("IncrementalBuildVerbose", "EvalLeafsFirst", fun _->sprintf "name=%s" output)
  672. let ExecuteApply action bt =
  673. let actionResult = Action.Execute(action)
  674. ApplyResult(actionResult,bt)
  675. let rec Eval(bt,gen) =
  676. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "---- Build generation %d ----" gen)
  677. #if DEBUG
  678. // This can happen, for example, if there is a task whose timestamp never stops increasing.
  679. // Possibly could detect this case directly.
  680. if gen>5000 then failwith "Infinite loop in incremental builder?"
  681. #endif
  682. let newBt = ForeachAction output bt ExecuteApply bt
  683. if newBt=bt then bt else Eval(newBt,gen+1)
  684. Eval(bt,0)
  685. let Step output (bt:Build) =
  686. use t = Trace.Call("IncrementalBuildVerbose", "Step", fun _->sprintf "name=%s" output)
  687. let BuildActionList() =
  688. use t = Trace.Call("IncrementalBuildVerbose", "BuildActionList", fun _->sprintf "name=%s" output)
  689. let Cons action list = action :: list
  690. // Hey look, we're building up the whole list, executing one thing and then throwing
  691. // the list away. What about saving the list inside the Build instance?
  692. ForeachAction output bt Cons []
  693. let ExecuteOneAction(worklist) =
  694. use t = Trace.Call("IncrementalBuildVerbose", "ExecuteOneAction", fun _->sprintf "name=%s" output)
  695. match worklist with
  696. | action::_ ->
  697. let actionResult = Action.Execute(action)
  698. Some(ApplyResult(actionResult,bt))
  699. | _->None
  700. ExecuteOneAction(BuildActionList())
  701. /// Eval by calling step over and over until done.
  702. let rec EvalStepwise output bt =
  703. use t = Trace.Call("IncrementalBuildVerbose", "EvalStepwise", fun _->sprintf "name=%s" output)
  704. let rec Evaluate(output,bt)=
  705. let newBt = Step output bt
  706. match newBt with
  707. | Some(newBt)-> Evaluate(output,newBt)
  708. | None->bt
  709. Evaluate(output,bt)
  710. // Note: this discards its slot. This causes TypecheckStates to be evaluated for all files
  711. // even if we only need one such state. This is especially noticeable on startup of
  712. // large solutions, where no intellisense is available until all files have been typechecked
  713. let EvalSlot(output,_,bt) = EvalLeafsFirst output bt
  714. let Eval = EvalLeafsFirst
  715. let GetScalarResult<'T>(name,bt) : ('T*DateTime) option =
  716. use t = Trace.Call("IncrementalBuildVerbose", "GetScalarResult", fun _->sprintf "name=%s" name)
  717. match GetTopLevelExprByName(bt,name) with
  718. | ScalarExpr(se)->
  719. let id = ScalarExpr.GetId(se)
  720. match bt.Results.TryFind(id) with
  721. | Some(result) ->
  722. match result with
  723. | ScalarResult(sr) ->
  724. match sr.TryGetAvailable() with
  725. | Some(r,timestamp,_) -> Some(downcast r, timestamp)
  726. | None -> None
  727. | _ ->failwith "Expected a scalar result."
  728. | None->None
  729. | VectorExpr _ -> failwith "Expected scalar."
  730. let GetVectorResult<'T>(name,bt) : 'T array =
  731. match GetTopLevelExprByName(bt,name) with
  732. | ScalarExpr _ -> failwith "Expected vector."
  733. | VectorExpr ve -> AvailableAllResultsOfExpr bt ve |> List.map(unbox) |> Array.ofList
  734. let GetVectorResultBySlot<'T>(name,slot,bt) : ('T*DateTime) option =
  735. match GetTopLevelExprByName(bt,name) with
  736. | ScalarExpr _ -> failwith "Expected vector expression"
  737. | VectorExpr ve ->
  738. match GetVectorExprResult(bt,ve,slot).TryGetAvailable() with
  739. | Some(o,timestamp,_) -> Some(downcast o,timestamp)
  740. | None->None
  741. /// Given an input value, find the corresponding slot.
  742. let GetSlotByInput<'T>(name:string,input:'T,build:Build,equals:'T->'T->bool) : int =
  743. let expr = GetExprByName(build,name)
  744. let id = Expr.GetId(expr)
  745. let resultSet = Option.get ( build.Results.TryFind(id))
  746. match resultSet with
  747. | VectorResult(rv)->
  748. let MatchNames acc (slot,result) =
  749. match result with
  750. | Available(o,_,_)->
  751. let o = o :?> 'T
  752. if equals o input then Some(slot) else acc
  753. | _ -> acc
  754. let slotOption = rv.FoldLeft MatchNames None
  755. match slotOption with
  756. | Some(slot) -> slot
  757. | _ -> failwith (sprintf "Could not find requested input '%A' named '%s' in set %+A" input name rv)
  758. | _ -> failwith (sprintf "Could not find requested input: %A" input)
  759. // Redeclare functions in the incremental build scope-----------------------------------------------------------------------
  760. // Methods for declaring inputs and outputs
  761. let InputVector<'T> name =
  762. let expr = VectorInput(NextId(),name,typeof<'T>)
  763. { new Vector<'T>
  764. interface IVector with
  765. override pe.GetVectorExpr() = expr }
  766. let InputScalar<'T> name =
  767. let expr = ScalarInput(NextId(),name)
  768. { new Scalar<'T>
  769. interface IScalar with
  770. override pe.GetScalarExpr() = expr }
  771. module Scalar =
  772. let Map (taskname:string) (task:'I->'O) (input:Scalar<'I>) : Scalar<'O> =
  773. let BoxingMap i = box(task(unbox(i)))
  774. let input = (input:?>IScalar).GetScalarExpr()
  775. let expr = ScalarMap(NextId(),taskname,input,BoxingMap)
  776. { new Scalar<'O>
  777. interface IScalar with
  778. override pe.GetScalarExpr() = expr}
  779. let Multiplex (taskname:string) (task:'I -> 'O array) (input:Scalar<'I>) : Vector<'O> =
  780. let BoxingMultiplex i = Array.map box (task(unbox(i)))
  781. let input = (input:?>IScalar).GetScalarExpr()
  782. let expr = VectorMultiplex(NextId(),taskname,input,BoxingMultiplex)
  783. { new Vector<'O>
  784. interface IVector with
  785. override pe.GetVectorExpr() = expr}
  786. module Vector =
  787. let Map (taskname:string) (task:'I ->'O) (input:Vector<'I>) : Vector<'O> =
  788. let BoxingMapVector i =
  789. box(task(unbox i))
  790. let input = (input:?>IVector).GetVectorExpr()
  791. let expr = VectorMap(NextId(),taskname,input,BoxingMapVector)
  792. { new Vector<'O>
  793. interface IVector with
  794. override pe.GetVectorExpr() = expr }
  795. let ScanLeft (taskname:string) (task:'A -> 'I -> Eventually<'A>) (acc:Scalar<'A>) (input:Vector<'I>) : Vector<'A> =
  796. let BoxingScanLeft a i =
  797. Eventually.box(task (unbox a) (unbox i))
  798. let acc = (acc:?>IScalar).GetScalarExpr()
  799. let input = (input:?>IVector).GetVectorExpr()
  800. let expr = VectorScanLeft(NextId(),taskname,acc,input,BoxingScanLeft)
  801. { new Vector<'A>
  802. interface IVector with
  803. override pe.GetVectorExpr() = expr }
  804. let Demultiplex (taskname:string) (task:'I array -> 'O) (input:Vector<'I>) : Scalar<'O> =
  805. let BoxingDemultiplex i =
  806. box(task (Array.map unbox i) )
  807. let input = (input:?>IVector).GetVectorExpr()
  808. let expr = ScalarDemultiplex(NextId(),taskname,input,BoxingDemultiplex)
  809. { new Scalar<'O>
  810. interface IScalar with
  811. override pe.GetScalarExpr() = expr }
  812. let Stamp (taskname:string) (task:'I -> DateTime) (input:Vector<'I>) : Vector<'I> =
  813. let BoxingTouch i =
  814. task(unbox i)
  815. let input = (input:?>IVector).GetVectorExpr()
  816. let expr = VectorStamp(NextId(),taskname,input,BoxingTouch)
  817. { new Vector<'I>
  818. interface IVector with
  819. override pe.GetVectorExpr() = expr }
  820. let AsScalar (taskname:string) (input:Vector<'I>) : Scalar<'I array> =
  821. Demultiplex taskname (fun v->v) input
  822. type BuildScope() =
  823. let outputs = ref []
  824. member b.DeclareScalarOutput(name,output:Scalar<'t>)=
  825. let output:IScalar = output:?>IScalar
  826. outputs := NamedScalarOutput(name,output) :: !outputs
  827. member b.DeclareVectorOutput(name,output:Vector<'t>)=
  828. let output:IVector = output:?>IVector
  829. outputs := NamedVectorOutput(name,output) :: !outputs
  830. member b.GetConcreteBuild(vectorinputs,scalarinputs) =
  831. ToBound(ToBuild(!outputs),vectorinputs,scalarinputs)
  832. // ------------------------------------------------------------------------------------------
  833. // The incremental build definition for parsing and typechecking F#
  834. // ------------------------------------------------------------------------------------------
  835. module internal FsiGeneration =
  836. open Internal.Utilities
  837. open Internal.Utilities.Collections
  838. open IncrementalBuild
  839. open Microsoft.FSharp.Compiler.Build
  840. open Microsoft.FSharp.Compiler.Fscopts
  841. open Microsoft.FSharp.Compiler.Ast
  842. open Microsoft.FSharp.Compiler.ErrorLogger
  843. open Microsoft.FSharp.Compiler.Env
  844. open Microsoft.FSharp.Compiler.TypeChecker
  845. open Microsoft.FSharp.Compiler.Tast
  846. open Microsoft.FSharp.Compiler.Range
  847. open Microsoft.FSharp.Compiler
  848. open Microsoft.FSharp.Compiler.AbstractIL.Internal
  849. module Tc = Microsoft.FSharp.Compiler.TypeChecker
  850. open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
  851. module Renderer =
  852. open Microsoft.FSharp.Compiler.Layout
  853. type Mapping = Dictionary<string list, int * int>
  854. let posTrackMappingBuildingR (header:string list option) (xySt:(int * int * Mapping)) (rend: ('a, 'b) render) : ('a * (int * int * Mapping), 'b * (int * int * Mapping)) render =
  855. { new render<_,_> with
  856. member r.Start () =
  857. let st = rend.Start ()
  858. let (x, y, m) = xySt
  859. let (x, y, st) =
  860. match header with
  861. | Some h -> let renderWithBreak st s =
  862. let st = rend.AddText st s
  863. rend.AddBreak st 0
  864. let st = List.fold renderWithBreak st h
  865. (0, y + List.length h, st)
  866. | None -> (x, y, st)
  867. (st, (x, y, m)) ;
  868. member r.AddText ((st, (x, y, m))) text = (rend.AddText st text, (x + text.Length, y, m)) ;
  869. member r.AddBreak ((st, (_, y, m))) n = (rend.AddBreak st n, (n, y + 1, m)) ;
  870. member r.AddTag ((st, ((x, y, m) as xySt))) (tag, attrs, start) =
  871. let addToMap k v =
  872. if m.ContainsKey(k) then () // this keeps the first binding that we find for an identifier
  873. else m.Add(k,v)
  874. if start && tag = "goto:path" then
  875. addToMap (List.map fst attrs) (x,y)
  876. (st, (x, y, m))
  877. else (rend.AddTag st (tag, attrs, start), xySt) ;
  878. member r.Finish ((st, (x, y, m))) = (rend.Finish st, (x, y, m)) }
  879. /// given:
  880. /// initial state : (x : int * y : int * Map<full path : string list, c : int * r : int>)
  881. /// render a GotoDefinition-annotated AST and return a final state (mapping
  882. /// fully-qualified names to (x, y) positions in the rendered file
  883. let showForGotoDefinition os showHeader st =
  884. let h =
  885. if showHeader
  886. then Some [ "// "^(FSComp.SR.gotoDefinitionHeader())
  887. "#light"
  888. ""
  889. ]
  890. else None
  891. posTrackMappingBuildingR h st (channelR os) |> renderL
  892. type FsiGenerationResult = (string * Dictionary<string list, int * int> * string list) option
  893. /// Compute a probably-safe directory where .fsi's can be generated without
  894. /// interfering with user files. We'll create a well-known-named directory
  895. /// in the system-reported temp path.
  896. #if SILVERLIGHT
  897. let PathForGeneratedVisualStudioFSharpTempFiles = ""
  898. #else
  899. let PathForGeneratedVisualStudioFSharpTempFiles =
  900. let p = Path.Combine (Path.GetTempPath (), "MicrosoftVisualStudioFSharpTemporaryFiles")
  901. if not (Directory.Exists p)
  902. then Directory.CreateDirectory p |> ignore
  903. p
  904. #endif
  905. /// For an assembly stored in `<fullpath-to>\<name>.dll`, generate the .fsi
  906. /// into `<project-path>\<name>.temp.fsi`
  907. let GeneratedFsiNameGenerator s =
  908. let baseName = PathForGeneratedVisualStudioFSharpTempFiles
  909. let extn = ".temp.fsi"
  910. s |> Path.GetFileName |> Filename.chopExtension |> (fun x -> x + extn) |> (fun n -> Path.Combine(baseName,n))
  911. /// Generate an F# signature file for an assembly; this is intended for
  912. /// use with GotoDefinition
  913. ///
  914. /// nameFixer is a function to convert filenames to a canonical form
  915. /// s is the name of the .dll for which an .fsi ought to be
  916. /// generated
  917. let GenerateFsiFile (tcConfig:TcConfig,tcGlobals,tcImports:TcImports,gotoCache) nameFixer s =
  918. let denv = DisplayEnv.Empty tcGlobals
  919. let denv = { denv with
  920. showImperativeTyparAnnotations = true ;
  921. showAttributes = true ; }
  922. let denv = denv.SetOpenPaths
  923. [ FSharpLib.RootPath
  924. FSharpLib.CorePath
  925. FSharpLib.CollectionsPath
  926. FSharpLib.ControlPath
  927. IL.splitNamespace FSharpLib.ExtraTopLevelOperatorsName ]
  928. let fixedName = nameFixer s
  929. match Map.tryFind fixedName !gotoCache with
  930. | Some (Some (outName, _, _) as res) when File.SafeExists outName -> res
  931. | Some None -> None
  932. | _ ->
  933. let res =
  934. let s = fixedName
  935. let outName = GeneratedFsiNameGenerator s
  936. let relevantCcus =
  937. tcImports.GetCcuInfos ()
  938. |> List.map (fun asm -> asm.FSharpViewOfMetadata)
  939. |> List.filter (fun ccu ->
  940. match ccu.FileName with
  941. | Some s' -> nameFixer s' = s
  942. | None -> false)
  943. let writeModul isFirst os st (ccu:CcuThunk) =
  944. ccu.Contents |> NicePrint.assemblyL denv |> Renderer.showForGotoDefinition os isFirst st |> snd
  945. match relevantCcus with
  946. | [] -> None
  947. | c :: cs ->
  948. if File.SafeExists outName
  949. then File.SetAttributes (outName, FileAttributes.Temporary)
  950. File.Delete outName
  951. let outFile = File.CreateText outName
  952. let outStrm = outFile :> System.IO.TextWriter
  953. let initSt = (0, 0, new Dictionary<_,_>())
  954. let st = writeModul true outStrm initSt c
  955. let (_, _, mapping) = List.fold (writeModul false outStrm) st cs
  956. outFile.Close ()
  957. File.SetAttributes (outName, FileAttributes.Temporary ||| FileAttributes.ReadOnly)
  958. Some (outName, mapping, tcConfig.referencedDLLs |> List.map (fun r -> nameFixer r.Text) )
  959. gotoCache := Map.add fixedName res !gotoCache
  960. res
  961. // ------------------------------------------------------------------------------------------
  962. // The incremental build definition for parsing and typechecking F#
  963. // ------------------------------------------------------------------------------------------
  964. module internal IncrementalFSharpBuild =
  965. open Internal.Utilities
  966. open Internal.Utilities.Collections
  967. open IncrementalBuild
  968. open Microsoft.FSharp.Compiler.Build
  969. open Microsoft.FSharp.Compiler.Fscopts
  970. open Microsoft.FSharp.Compiler.Ast
  971. open Microsoft.FSharp.Compiler.ErrorLogger
  972. open Microsoft.FSharp.Compiler.Env
  973. open Microsoft.FSharp.Compiler.TypeChecker
  974. open Microsoft.FSharp.Compiler.Tast
  975. open Microsoft.FSharp.Compiler.Range
  976. open Microsoft.FSharp.Compiler
  977. open Microsoft.FSharp.Compiler.AbstractIL.Internal
  978. module Tc = Microsoft.FSharp.Compiler.TypeChecker
  979. open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
  980. // This type is designed to be a lightweight way to instrument the most recent filenames that the
  981. // IncrementalBuilder did a parse/typecheck of, so we can more easily unittest/debug the
  982. // 'incremental' behavior of the product.
  983. type internal FixedLengthMRU<'T>() =
  984. let MAX = 40 // Length of the MRU. For our current unit tests, 40 is enough.
  985. let data : ('T option)[] = Array.create MAX None
  986. let mutable curIndex = 0
  987. let mutable numAdds = 0
  988. // called by the product, to note when a parse/typecheck happens for a file
  989. member this.Add(filename:'T) =
  990. numAdds <- numAdds + 1
  991. data.[curIndex] <- Some filename
  992. curIndex <- curIndex + 1
  993. if curIndex = MAX then
  994. curIndex <- 0
  995. member this.CurrentEventNum = numAdds
  996. // called by unit tests, returns 'n' most recent additions.
  997. member this.MostRecentList(n:int) : list<'T> =
  998. if n < 0 || n > MAX then
  999. raise <| new System.ArgumentOutOfRangeException("n", sprintf "n must be between 0 and %d, inclusive, but got %d" MAX n)
  1000. let mutable remaining = n
  1001. let mutable s = []
  1002. let mutable i = curIndex - 1
  1003. while remaining <> 0 do
  1004. if i < 0 then
  1005. i <- MAX - 1
  1006. match data.[i] with
  1007. | None -> ()
  1008. | Some x -> s <- x :: s
  1009. i <- i - 1
  1010. remaining <- remaining - 1
  1011. List.rev s
  1012. type IBEvent =
  1013. | IBEParsed of string // filename
  1014. | IBETypechecked of string // filename
  1015. | IBEDeleted
  1016. let IncrementalBuilderEventsMRU = new FixedLengthMRU<IBEvent>()
  1017. let GetMostRecentIncrementalBuildEvents(n) = IncrementalBuilderEventsMRU.MostRecentList(n)
  1018. let GetCurrentIncrementalBuildEventNum() = IncrementalBuilderEventsMRU.CurrentEventNum
  1019. /// Callbacks for things that happen in the build.
  1020. type BuildEvents =
  1021. { BeforeTypeCheckFile: string -> unit }
  1022. static member Default = { BeforeTypeCheckFile = ignore}
  1023. type FileDependency = {
  1024. // Name of the file
  1025. Filename : string
  1026. // If true, then deletion or creation of this file should trigger an entirely fresh build
  1027. ExistenceDependency : bool
  1028. // If true, then changing this file should trigger and call to incrementally build
  1029. IncrementalBuildDependency : bool } with
  1030. override this.ToString() =
  1031. sprintf "FileDependency(%s,existence=%A,incremental=%A)" this.Filename this.ExistenceDependency this.IncrementalBuildDependency
  1032. type Dependencies = {
  1033. ImportedCcusInvalidated : IEvent<string>
  1034. Files : FileDependency list
  1035. }
  1036. /// Accumulated results of type checking.
  1037. [<NoEquality; NoComparison>]
  1038. type TypeCheckAccumulator = {
  1039. tcState: TcState;
  1040. tcImports:TcImports;
  1041. tcGlobals:TcGlobals;
  1042. tcConfig:TcConfig;
  1043. tcEnv: TcEnv;
  1044. topAttribs:TopAttribs option;
  1045. typedImplFiles:TypedImplFile list;
  1046. errors:(PhasedError * bool) list // errors=true, warnings=false
  1047. }
  1048. /// Maximum time share for a piece of background work before it should (cooperatively) yield
  1049. /// to enable other requests to be serviced. Yielding means returning a continuation function
  1050. /// (via an Eventually<_> value of case NotYetDone) that can be called as the next piece of work.
  1051. let maxTimeShareMilliseconds =
  1052. #if SILVERLIGHT
  1053. 50L
  1054. #else
  1055. match System.Environment.GetEnvironmentVariable("mFSharp_MaxTimeShare") with
  1056. | null | "" -> 50L
  1057. | s -> int64 s
  1058. #endif
  1059. /// Global service state
  1060. type FrameworkImportsCacheKey = (*resolvedpath*)string list * string * (*ClrRoot*)string list* (*fsharpBinaries*)string
  1061. let private frameworkTcImportsCache = AgedLookup<FrameworkImportsCacheKey,(TcGlobals * TcImports)>(8, areSame=(fun (x,y) -> x = y))
  1062. /// This function strips the "System" assemblies from the tcConfig and returns a age-cached TcImports for them.
  1063. let GetFrameworkTcImports(tcConfig:TcConfig) =
  1064. // Split into installed and not installed.
  1065. let frameworkDLLs,nonFrameworkResolutions,unresolved = TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig)
  1066. let frameworkDLLsKey =
  1067. frameworkDLLs
  1068. |> List.map(fun ar->ar.resolvedPath) // The cache key. Just the minimal data.
  1069. |> List.sort // Sort to promote cache hits.
  1070. let tcGlobals,frameworkTcImports =
  1071. // Prepare the frameworkTcImportsCache
  1072. //
  1073. // The data elements in this key are very important. There should be nothing else in the TcConfig that logically affects
  1074. // the import of a set of framework DLLs into F# CCUs. That is, the F# CCUs that result from a set of DLLs (including
  1075. // FSharp.Core.dll andb mscorlib.dll) must be logically invariant of all the other compiler configuration parameters.
  1076. let key = (frameworkDLLsKey,
  1077. tcConfig.mscorlibAssemblyName,
  1078. tcConfig.ClrRoot,
  1079. tcConfig.fsharpBinariesDir)
  1080. match frameworkTcImportsCache.TryGet key with
  1081. | Some(res)-> res
  1082. | None ->
  1083. let tcConfigP = TcConfigProvider.Constant(tcConfig)
  1084. let ((tcGlobals,tcImports) as res) = TcImports.BuildFrameworkTcImports (tcConfigP,frameworkDLLs,nonFrameworkResolutions)
  1085. frameworkTcImportsCache.Put(key,res)
  1086. tcGlobals,tcImports
  1087. tcGlobals,frameworkTcImports,nonFrameworkResolutions,unresolved
  1088. //------------------------------------------------------------------------------------
  1089. // Rules for reactive building.
  1090. //
  1091. // This phrases the compile as a series of vector functions and vector manipulations.
  1092. // Rules written in this language are then transformed into a plan to execute the
  1093. // various steps of the process (possible in parallel).
  1094. //-----------------------------------------------------------------------------------
  1095. let Create (tcConfig : TcConfig, projectDirectory : string, assemblyName, niceNameGen, resourceManager,
  1096. sourceFiles:string list, ensureReactive, buildEvents:BuildEvents, errorLogger:ErrorLogger,
  1097. _errorRecovery : exn -> range -> unit)
  1098. =
  1099. use t = Trace.Call("IncrementalBuildVerbose", "Create", fun _ -> sprintf " tcConfig.includes = %A" tcConfig.includes)
  1100. let tcConfigP = TcConfigProvider.Constant(tcConfig)
  1101. let importsInvalidated = new Event<_>()
  1102. /// An error logger that captures errors and eventually sends a single error or warning for all the errors and warning in a file
  1103. let CompilationErrorLogger _sourceRange =
  1104. let warningsSeenInScope = new ResizeArray<_>()
  1105. let errorsSeenInScope = new ResizeArray<_>()
  1106. let warningOrError warn exn =
  1107. let warn = warn && not (ReportWarningAsError tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn tcConfig.specificWarnAsError tcConfig.specificWarnAsWarn tcConfig.globalWarnAsError exn)
  1108. if not warn then
  1109. errorsSeenInScope.Add(exn)
  1110. errorLogger.ErrorSink(exn)
  1111. else if ReportWarning tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn exn then
  1112. warningsSeenInScope.Add(exn)
  1113. errorLogger.WarnSink(exn)
  1114. let errorLogger =
  1115. { new ErrorLogger with
  1116. member x.WarnSink(exn) = warningOrError true exn
  1117. member x.ErrorSink(exn) = warningOrError false exn
  1118. member x.ErrorCount = errorLogger.ErrorCount }
  1119. let returnErrors() =
  1120. let errorsAndWarnings = (errorsSeenInScope |> ResizeArray.toList |> List.map(fun e->e,true)) @ (warningsSeenInScope |> ResizeArray.toList |> List.map(fun e->e,false))
  1121. errorsAndWarnings
  1122. // Return the error logger and a function to run when we want the errors reported
  1123. errorLogger,returnErrors
  1124. /// Use to reset error and warning handlers
  1125. let CompilationGlobalsScope(errorLogger,phase) =
  1126. let savedEnvSink = !(Nameres.GlobalTypecheckResultsSink)
  1127. Nameres.GlobalTypecheckResultsSink := None
  1128. ignore projectDirectory
  1129. let unwindEL = PushErrorLoggerPhaseUntilUnwind(fun _ -> errorLogger)
  1130. let unwindBP = PushThreadBuildPhaseUntilUnwind (phase)
  1131. // Return the disposable object that cleans up
  1132. {new IDisposable with
  1133. member d.Dispose() =
  1134. unwindBP.Dispose();
  1135. unwindEL.Dispose();
  1136. Nameres.GlobalTypecheckResultsSink:=savedEnvSink}
  1137. let CompilationGlobalsAndErrorLoggerScopeWithSourceRange(sourceRange,phase) =
  1138. let errorLogger,returnErrors = CompilationErrorLogger(sourceRange)
  1139. // Return the disposable object that cleans up
  1140. errorLogger,returnErrors,CompilationGlobalsScope(errorLogger,phase)
  1141. let CompilationGlobalsAndErrorLoggerScope(phase) =
  1142. CompilationGlobalsAndErrorLoggerScopeWithSourceRange(rangeStartup,phase)
  1143. // Strip out and cache a level of "system" references.
  1144. let tcGlobals,frameworkTcImports,nonFrameworkResolutions,unresolvedReferences = GetFrameworkTcImports(tcConfig)
  1145. // Check for the existence of loaded sources and prepend them to the sources list if present.
  1146. let sourceFiles = tcConfig.GetAvailableLoadedSources() @ (sourceFiles|>List.map(fun s -> rangeStartup,s))
  1147. // Mark up the source files with an indicator flag indicating if they are the last source file in the project
  1148. let sourceFiles =
  1149. let flags = tcConfig.ComputeCanContainEntryPoint(sourceFiles |> List.map snd)
  1150. (sourceFiles,flags) ||> List.map2 (fun (m,nm) flag -> (m,nm,flag))
  1151. // Get the original referenced assembly names
  1152. // System.Diagnostics.Debug.Assert(not((sprintf "%A" nonFrameworkResolutions).Contains("System.dll")),sprintf "Did not expect a system import here. %A" nonFrameworkResolutions)
  1153. /// Get the timestamp of the given file name.
  1154. let StampFilename (_m:range, filename:string, _isLastCompiland:bool) =
  1155. File.GetLastWriteTimeShim(filename)
  1156. /// Parse the given files and return the given inputs. This function is expected to be
  1157. /// able to be called with a subset of sourceFiles and return the corresponding subset of
  1158. /// parsed inputs.
  1159. let Parse (sourceRange:range,filename:string,isLastCompiland) =
  1160. let errorLogger, returnErrors, sDisposable = CompilationGlobalsAndErrorLoggerScopeWithSourceRange(sourceRange, BuildPhase.Parse)
  1161. use s = sDisposable
  1162. Trace.Print("FSharpBackgroundBuild", fun _ -> sprintf "Parsing %s..." filename)
  1163. try
  1164. IncrementalBuilderEventsMRU.Add(IBEParsed filename)
  1165. let result = ParseOneInputFile(tcConfig,resourceManager,[],filename ,isLastCompiland,errorLogger,(*retryLocked*)true)
  1166. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "done.")
  1167. result,sourceRange,filename,returnErrors()
  1168. with e ->
  1169. System.Diagnostics.Debug.Assert(false, sprintf "unexpected failure in IncrementalFSharpBuild.Parse\nerror = %s" (e.ToString()))
  1170. failwith "last chance failure"
  1171. /// Get the names of all referenced assemblies.
  1172. let GetReferencedAssemblyNames() : (range*string*DateTime) array =
  1173. let errorLogger, _returnErrors, sDisposable = CompilationGlobalsAndErrorLoggerScope(BuildPhase.Parameter)
  1174. use s = sDisposable
  1175. let result =
  1176. nonFrameworkResolutions
  1177. |> List.map(fun r ->
  1178. let originaltimestamp =
  1179. try
  1180. if File.SafeExists(r.resolvedPath) then
  1181. let result = File.GetLastWriteTimeShim(r.resolvedPath)
  1182. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Found referenced assembly '%s'.\n" r.resolvedPath)
  1183. result
  1184. else
  1185. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Did not find referenced assembly '%s' on disk.\n" r.resolvedPath)
  1186. DateTime.Now
  1187. with e ->
  1188. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Did not find referenced assembly '%s' due to exception.\n" r.resolvedPath)
  1189. errorLogger.Warning(e)
  1190. DateTime.Now
  1191. r.originalReference.Range,r.resolvedPath,originaltimestamp)
  1192. |> List.toArray
  1193. result
  1194. /// Timestamps of referenced assemblies are taken from the file's timestamp.
  1195. let TimestampReferencedAssembly (_range, filename, originaltimestamp) =
  1196. let errorLogger, _returnErrors, sDisposable = CompilationGlobalsAndErrorLoggerScope(BuildPhase.Parameter) // Parameter because -r reference
  1197. use s = sDisposable
  1198. let timestamp =
  1199. try
  1200. if File.SafeExists(filename) then
  1201. let ts = File.GetLastWriteTimeShim(filename)
  1202. if ts<>originaltimestamp then
  1203. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing change in timestamp of file %s from %A to %A" filename originaltimestamp ts)
  1204. else
  1205. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing no change in timestamp of file %s (still %A)" filename originaltimestamp)
  1206. ts
  1207. else
  1208. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Noticing that file %s was deleted, but ignoring that for timestamp checking" filename)
  1209. originaltimestamp
  1210. with e ->
  1211. // For example, malformed filename
  1212. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Exception when checking stamp of file %s, using old stamp %A" filename originaltimestamp)
  1213. errorLogger.Warning(e)
  1214. originaltimestamp
  1215. timestamp
  1216. // Link all the assemblies together and produce the input typecheck accumulator
  1217. let CombineImportedAssemblies _ : TypeCheckAccumulator =
  1218. let errorLogger, returnErrors, sDisposable = CompilationGlobalsAndErrorLoggerScope(BuildPhase.Parameter)
  1219. use s = sDisposable
  1220. let tcImports =
  1221. try
  1222. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "About to (re)create tcImports")
  1223. let tcImports = TcImports.BuildNonFrameworkTcImports(tcConfigP,tcGlobals,frameworkTcImports,nonFrameworkResolutions)
  1224. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "(Re)created tcImports")
  1225. tcImports
  1226. with e ->
  1227. System.Diagnostics.Debug.Assert(false, sprintf "Could not BuildAllReferencedDllTcImports %A" e)
  1228. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> "Failed to recreate tcImports\n %A")
  1229. errorLogger.Warning(e)
  1230. frameworkTcImports
  1231. let tcEnv0 = GetInitialTypecheckerEnv (Some assemblyName) rangeStartup tcConfig tcImports tcGlobals
  1232. let tcState0 = TypecheckInitialState (rangeStartup,assemblyName,tcConfig,tcGlobals,niceNameGen,tcEnv0)
  1233. let tcAcc = {
  1234. tcGlobals=tcGlobals
  1235. tcImports=tcImports
  1236. tcState=tcState0
  1237. tcConfig=tcConfig
  1238. tcEnv=tcEnv0
  1239. topAttribs=None
  1240. typedImplFiles=[]
  1241. errors=returnErrors()
  1242. }
  1243. tcAcc
  1244. /// Type check all files.
  1245. let TypeCheck (tcAcc:TypeCheckAccumulator) input : Eventually<TypeCheckAccumulator> =
  1246. match input with
  1247. | Some(input),sourceRange,filename,parseErrors->
  1248. IncrementalBuilderEventsMRU.Add(IBETypechecked filename)
  1249. let errorLogger,reportErrors = CompilationErrorLogger(sourceRange)
  1250. let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false,GetScopedPragmasForInput(input),errorLogger)
  1251. let tcAcc = {tcAcc with errors = parseErrors}
  1252. let fullComputation =
  1253. eventually {
  1254. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> sprintf "Typechecking %s..." filename)
  1255. buildEvents.BeforeTypeCheckFile(filename)
  1256. let! (tcEnv,topAttribs,typedImplFiles),tcState = TypecheckOneInputEventually (fun () -> errorLogger.ErrorCount = 0) tcConfig tcAcc.tcImports false tcAcc.tcGlobals None tcAcc.tcState input
  1257. Trace.PrintLine("FSharpBackgroundBuild", fun _ -> sprintf "done.")
  1258. return {tcAcc with tcState=tcState; tcEnv=tcEnv; topAttribs=Some(topAttribs); typedImplFiles=typedImplFiles; errors = tcAcc.errors @ (reportErrors()) }
  1259. }
  1260. // Run part of the Eventually<_> computation until a timeout is reached. If not complete,
  1261. // return a new Eventually<_> computation which recursively runs more of the computation.
  1262. // - When the whole thing is finished commit the error results sent through the errorLogger.
  1263. // - Each time we do real work we reinstall the CompilationGlobalsScope
  1264. if ensureReactive then
  1265. let timeSlicedComputation =
  1266. fullComputation |>
  1267. Eventually.repeatedlyProgressUntilDoneOrTimeShareOver
  1268. maxTimeShareMilliseconds
  1269. (fun f ->
  1270. // Reinstall the compilation globals each time we start or restart
  1271. use unwind = CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck)
  1272. Trace.Print("FSharpBackgroundBuildVerbose", fun _ -> sprintf "continuing %s.\n" filename)
  1273. f()
  1274. (* unwind dispose *)
  1275. )
  1276. timeSlicedComputation
  1277. else
  1278. use unwind = CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck)
  1279. fullComputation |> Eventually.force |> Eventually.Done
  1280. | _ ->
  1281. Eventually.Done tcAcc
  1282. /// Finish up the typechecking to produce outputs for the rest of the compilation process
  1283. let FinalizeTypeCheck (tcStates:TypeCheckAccumulator array) =
  1284. Trace.PrintLine("FSharpBackgroundBuildVerbose", fun _ -> sprintf "Finalizing Type Check" )
  1285. let finalAcc = tcStates.[tcStates.Length-1]
  1286. let results : (TcEnv * TopAttribs * TypedImplFile list) list = tcStates |> List.ofArray |> List.map (fun acc-> acc.tcEnv, (Option.get acc.topAttribs), acc.typedImplFiles)
  1287. let (tcEnvAtEndOfLastFile,topAttrs,mimpls),tcState = TypecheckMultipleInputsFinish (results,finalAcc.tcState)
  1288. let tcState,tassembly = TypecheckClosedInputSetFinish (mimpls,tcState)
  1289. tcState, topAttrs, tassembly, tcEnvAtEndOfLastFile, finalAcc.tcImports, finalAcc.tcGlobals, finalAcc.tcConfig
  1290. let gotoCache = ref (Map.empty : Map<string, FsiGeneration.FsiGenerationResult>) // avoid regenerating the same file
  1291. let unresolvedFileDependencies =
  1292. unresolvedReferences
  1293. |> List.map (function Microsoft.FSharp.Compiler.Build.UnresolvedReference(referenceText, _) -> referenceText)
  1294. |> List.map (fun file->{Filename = file; ExistenceDependency = true; IncrementalBuildDependency = true })
  1295. let resolvedFileDependencies =
  1296. nonFrameworkResolutions |> List.map (fun r -> {Filename = r.resolvedPath ; ExistenceDependency = true; IncrementalBuildDependency = true })
  1297. let sourceFileDependencies =
  1298. sourceFiles |> List.map (fun (_,f,_) -> {Filename = f ; ExistenceDependency = true; IncrementalBuildDependency = true })
  1299. let fileDependencies = List.concat [unresolvedFileDependencies;resolvedFileDependencies;sourceFileDependencies]
  1300. #if DEBUG
  1301. resolvedFileDependencies |> List.iter (fun x -> System.Diagnostics.Debug.Assert(System.IO.Path.IsPathRootedShim(x.Filename), sprintf "file dependency should be absolute path: '%s'" x.Filename))
  1302. #endif
  1303. // ---------------------------------------------------------------------------------------------
  1304. let build = new BuildScope ()
  1305. // Inputs
  1306. let filenames = InputVector<range*string*bool> "Filenames"
  1307. let referencedAssemblies = InputVector<range*string*DateTime> "ReferencedAssemblies"
  1308. // Build
  1309. let stampedFilenames = Vector.Stamp "SourceFileTimeStamps" StampFilename filenames
  1310. let parseTrees = Vector.Map "Parse" Parse stampedFilenames
  1311. let stampedReferencedAssemblies = Vector.Stamp "TimestampReferencedAssembly" TimestampReferencedAssembly referencedAssemblies
  1312. let initialTcAcc = Vector.Demultiplex "CombineImportedAssemblies" CombineImportedAssemblies stampedReferencedAssemblies
  1313. let tcStates = Vector.ScanLeft "TypeCheck" TypeCheck initialTcAcc parseTrees
  1314. let finalizedTypeCheck = Vector.Demultiplex "FinalizeTypeCheck" FinalizeTypeCheck tcStates
  1315. let generatedSignatureFiles = Scalar.Map "GenerateSignatureFiles" (fun tcAcc -> FsiGeneration.GenerateFsiFile(tcAcc.tcConfig,tcAcc.tcGlobals, tcAcc.tcImports,gotoCache)) initialTcAcc
  1316. // Outputs
  1317. build.DeclareVectorOutput ("ParseTrees", parseTrees)
  1318. build.DeclareVectorOutput ("TypeCheckingStates",tcStates)
  1319. build.DeclareScalarOutput ("InitialTcAcc", initialTcAcc)
  1320. build.DeclareScalarOutput ("FinalizeTypeCheck", finalizedTypeCheck)
  1321. build.DeclareScalarOutput ("GenerateSignatureFiles", generatedSignatureFiles)
  1322. // ---------------------------------------------------------------------------------------------
  1323. let assems = GetReferencedAssemblyNames()
  1324. IncrementalBuilderEventsMRU.Add(IBEDeleted)
  1325. let build =
  1326. build.GetConcreteBuild (["Filenames", sourceFiles.Length, sourceFiles |> List.map box
  1327. "ReferencedAssemblies", assems.Length, assems |> Array.toList |> List.map box
  1328. ], [])
  1329. let dependencies = { ImportedCcusInvalidated = importsInvalidated.Publish; Files = fileDependencies }
  1330. build, dependencies
  1331. // Expose methods to operate on F# build in a strongly typed way----------------------------------
  1332. let Step(build) =
  1333. IncrementalBuild.Step "TypeCheckingStates" build
  1334. let EvalTypeCheckSlot(slotOfFile,build) =
  1335. let build = EvalSlot("InitialTcAcc",slotOfFile,build)
  1336. let build = EvalSlot("TypeCheckingStates",slotOfFile,build)
  1337. build
  1338. let GetAntecedentTypeCheckResultsBySlot(slotOfFile,build) =
  1339. let result =
  1340. match slotOfFile with
  1341. | (*first file*) 0 -> GetScalarResult<TypeCheckAccumulator>("InitialTcAcc",build)
  1342. | _ -> GetVectorResultBySlot<TypeCheckAccumulator>("TypeCheckingStates",slotOfFile-1,build)
  1343. match result with
  1344. | Some({tcState=tcState; tcGlobals=tcGlobals; tcConfig=tcConfig; tcImports=tcImports; errors=errors},timestamp)->
  1345. Some(tcState,tcImports,tcGlobals,tcConfig,errors,timestamp)
  1346. | _->None
  1347. let TypeCheck(build) =
  1348. let build = IncrementalBuild.Eval "FinalizeTypeCheck" build
  1349. match GetScalarResult<Build.TcState * TypeChecker.TopAttribs * Tast.TypedAssembly * TypeChecker.TcEnv * Build.TcImports * Env.TcGlobals * Build.TcConfig>("FinalizeTypeCheck",build) with
  1350. | Some((tcState,topAttribs,typedAssembly,tcEnv,tcImports,tcGlobals,tcConfig),_)->build,tcState,topAttribs,typedAssembly,tcEnv,tcImports,tcGlobals,tcConfig
  1351. | None -> failwith "Build was not evaluated."
  1352. let GetSlotOfFileName(filename:string,build:Build) =
  1353. // Get the slot of the given file and force it to build.
  1354. let CompareFileNames (_,f1,_) (_,f2,_) =
  1355. let result =
  1356. System.String.Compare(f1,f2,StringComparison.CurrentCultureIgnoreCase)=0
  1357. || System.String.Compare(Path.GetFullPathShim(f1),Path.GetFullPathShim(f2),StringComparison.CurrentCultureIgnoreCase)=0
  1358. result
  1359. GetSlotByInput("Filenames",(rangeStartup,filename,false),build,CompareFileNames)
  1360. let GetSlotsCount (build:Build) =
  1361. let expr = GetExprByName(build,"Filenames")
  1362. let id = Expr.GetId(expr)
  1363. match build.Results.TryFind(id) with
  1364. | Some(VectorResult vr) -> vr.Size
  1365. | _ -> failwith "Cannot know sizes"
  1366. let rec GetParseResultsBySlot (slot,build:Build) =
  1367. let result = GetVectorResultBySlot<Ast.Input option * Range.range * string>("ParseTrees",slot,build)
  1368. match result with
  1369. | Some ((inputOpt,range,fileName), _) -> inputOpt, range, fileName, build
  1370. | None ->
  1371. let build = IncrementalBuild.Eval "ParseTrees" build
  1372. GetParseResultsBySlot (slot,build)
  1373. /// Get a list of on-demand generators of F# signature files for referenced assemblies.
  1374. let GetFsiGenerators (build : Build) : ((string -> string) -> string -> FsiGeneration.FsiGenerationResult) * Build =
  1375. let build = IncrementalBuild.Eval "GenerateSignatureFiles" build
  1376. let gens = match IncrementalBuild.GetScalarResult<_> ("GenerateSignatureFiles", build) with
  1377. | Some (gens, _) -> gens
  1378. | None -> failwith "Build was not evaluated."
  1379. (gens, build)