illib.fs 53.9 KB
Newer Older
1
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
L
latkin 已提交
2

D
Don Syme 已提交
3
module public FSharp.Compiler.AbstractIL.Internal.Library 
L
latkin 已提交
4 5 6 7 8
#nowarn "1178" // The struct, record or union type 'internal_instr_extension' is not structurally comparable because the type


open System
open System.Collections.Generic
9 10
open System.Diagnostics
open System.IO
D
Don Syme 已提交
11
open System.Reflection
12
open System.Threading
13
open System.Runtime.CompilerServices
L
latkin 已提交
14 15 16

// Logical shift right treating int32 as unsigned integer.
// Code that uses this should probably be adjusted to use unsigned integer types.
17
let (>>>&) (x: int32) (n: int32) = int32 (uint32 x >>> n)
L
latkin 已提交
18

19
let notlazy v = Lazy<_>.CreateFromValue v
L
latkin 已提交
20

21
let inline isNil l = List.isEmpty l
22 23 24 25 26 27 28 29 30 31 32 33 34 35

/// Returns true if the list has less than 2 elements. Otherwise false.
let inline isNilOrSingleton l =
    match l with
    | [] 
    | [_] -> true
    | _ -> false

/// Returns true if the list contains exactly 1 element. Otherwise false.
let inline isSingleton l =
    match l with
    | [_] -> true
    | _ -> false

S
Steffen Forkmann 已提交
36
let inline isNonNull x = not (isNull x)
37

N
ncave 已提交
38
let inline nonNull msg x = if isNull x then failwith ("null: " + msg) else x
39

S
Steffen Forkmann 已提交
40
let inline (===) x y = LanguagePrimitives.PhysicalEquality x y
L
latkin 已提交
41

42
/// Per the docs the threshold for the Large Object Heap is 85000 bytes: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap#how-an-object-ends-up-on-the-large-object-heap-and-how-gc-handles-them
43 44
/// We set the limit to be 80k to account for larger pointer sizes for when F# is running 64-bit.
let LOH_SIZE_THRESHOLD_BYTES = 80_000
45

46 47 48 49
//---------------------------------------------------------------------
// Library: ReportTime
//---------------------------------------------------------------------
let reportTime =
50 51
    let mutable tFirst =None
    let mutable tPrev = None
52 53
    fun showTimes descr ->
        if showTimes then 
54
            let t = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds
55 56
            let prev = match tPrev with None -> 0.0 | Some t -> t
            let first = match tFirst with None -> (tFirst <- Some t; t) | Some t -> t
D
Don Syme 已提交
57
            printf "ilwrite: TIME %10.3f (total)   %10.3f (delta) - %s\n" (t - first) (t - prev) descr
58
            tPrev <- Some t
K
KevinRansom 已提交
59

L
latkin 已提交
60 61 62 63
//-------------------------------------------------------------------------
// Library: projections
//------------------------------------------------------------------------

64 65 66
[<Struct>]
/// An efficient lazy for inline storage in a class type. Results in fewer thunks.
type InlineDelayInit<'T when 'T : not struct> = 
67
    new (f: unit -> 'T) = {store = Unchecked.defaultof<'T>; func = Func<_>(f) } 
68
    val mutable store : 'T
69
    val mutable func : Func<'T>
70

71 72 73 74
    member x.Value = 
        match x.func with 
        | null -> x.store 
        | _ -> 
75
        let res = LazyInitializer.EnsureInitialized(&x.store, x.func) 
76 77 78 79
        x.func <- Unchecked.defaultof<_>
        res

//-------------------------------------------------------------------------
L
latkin 已提交
80 81 82 83 84 85 86 87 88
// Library: projections
//------------------------------------------------------------------------

let foldOn p f z x = f z (p x)

let notFound() = raise (KeyNotFoundException())

module Order = 
    let orderBy (p : 'T -> 'U) = 
89
        { new IComparer<'T> with member __.Compare(x, xx) = compare (p x) (p xx) }
L
latkin 已提交
90 91

    let orderOn p (pxOrder: IComparer<'U>) = 
92
        { new IComparer<'T> with member __.Compare(x, xx) = pxOrder.Compare (p x, p xx) }
L
latkin 已提交
93

94
    let toFunction (pxOrder: IComparer<'U>) x y = pxOrder.Compare(x, y)
L
latkin 已提交
95 96

//-------------------------------------------------------------------------
97
// Library: arrays, lists, options, resizearrays
L
latkin 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110
//-------------------------------------------------------------------------

module Array = 

    let mapq f inp =
        match inp with
        | [| |] -> inp
        | _ -> 
            let res = Array.map f inp 
            let len = inp.Length 
            let mutable eq = true
            let mutable i = 0 
            while eq && i < len do 
111
                if not (inp.[i] === res.[i]) then eq <- false
L
latkin 已提交
112 113 114 115 116 117 118 119 120
                i <- i + 1
            if eq then inp else res

    let lengthsEqAndForall2 p l1 l2 = 
        Array.length l1 = Array.length l2 &&
        Array.forall2 p l1 l2

    let order (eltOrder: IComparer<'T>) = 
        { new IComparer<array<'T>> with 
121
              member __.Compare(xs, ys) = 
L
latkin 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
                  let c = compare xs.Length ys.Length 
                  if c <> 0 then c else
                  let rec loop i = 
                      if i >= xs.Length then 0 else
                      let c = eltOrder.Compare(xs.[i], ys.[i])
                      if c <> 0 then c else
                      loop (i+1)
                  loop 0 }

    let existsOne p l = 
        let rec forallFrom p l n =
          (n >= Array.length l) || (p l.[n] && forallFrom p l (n+1))

        let rec loop p l n =
            (n < Array.length l) && 
            (if p l.[n] then forallFrom (fun x -> not (p x)) l (n+1) else loop p l (n+1))
          
        loop p l 0

141
    let existsTrue (arr: bool[]) = 
142
        let rec loop n = (n < arr.Length) && (arr.[n] || loop (n+1))
143
        loop 0
L
latkin 已提交
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    
    let findFirstIndexWhereTrue (arr: _[]) p = 
        let rec look lo hi = 
            assert ((lo >= 0) && (hi >= 0))
            assert ((lo <= arr.Length) && (hi <= arr.Length))
            if lo = hi then lo
            else
                let i = (lo+hi)/2
                if p arr.[i] then 
                    if i = 0 then i 
                    else
                        if p arr.[i-1] then 
                            look lo i
                        else 
                            i
                else
                    // not true here, look after
                    look (i+1) hi
        look 0 arr.Length
      
164 165 166
    /// pass an array byref to reverse it in place
    let revInPlace (array: 'T []) =
        if Array.isEmpty array then () else
E
Eugene Auduchinok 已提交
167 168
        let arrLen, revLen = array.Length-1, array.Length/2 - 1
        for idx in 0 .. revLen do
169
            let t1 = array.[idx] 
E
Eugene Auduchinok 已提交
170
            let t2 = array.[arrLen-idx]
171
            array.[idx] <- t2
E
Eugene Auduchinok 已提交
172
            array.[arrLen-idx] <- t1
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

    /// Async implementation of Array.map.
    let mapAsync (mapping : 'T -> Async<'U>) (array : 'T[]) : Async<'U[]> =
        let len = Array.length array
        let result = Array.zeroCreate len

        async { // Apply the mapping function to each array element.
            for i in 0 .. len - 1 do
                let! mappedValue = mapping array.[i]
                result.[i] <- mappedValue

            // Return the completed results.
            return result
        }
        
    /// Returns a new array with an element replaced with a given value.
    let replace index value (array: _ []) =
        if index >= array.Length then raise (IndexOutOfRangeException "index")
        let res = Array.copy array
        res.[index] <- value
        res

    /// Optimized arrays equality. ~100x faster than `array1 = array2` on strings.
    /// ~2x faster for floats
    /// ~0.8x slower for ints
    let inline areEqual (xs: 'T []) (ys: 'T []) =
        match xs, ys with
        | null, null -> true
        | [||], [||] -> true
        | null, _ | _, null -> false
        | _ when xs.Length <> ys.Length -> false
        | _ ->
            let mutable break' = false
            let mutable i = 0
            let mutable result = true
            while i < xs.Length && not break' do
                if xs.[i] <> ys.[i] then 
                    break' <- true
                    result <- false
                i <- i + 1
            result

    /// Returns all heads of a given array.
    /// For [|1;2;3|] it returns [|[|1; 2; 3|]; [|1; 2|]; [|1|]|]
    let heads (array: 'T []) =
        let res = Array.zeroCreate<'T[]> array.Length
        for i = array.Length - 1 downto 0 do
            res.[i] <- array.[0..i]
        res

    /// check if subArray is found in the wholeArray starting 
    /// at the provided index
    let inline isSubArray (subArray: 'T []) (wholeArray:'T []) index = 
        if isNull subArray || isNull wholeArray then false
        elif subArray.Length = 0 then true
        elif subArray.Length > wholeArray.Length then false
        elif subArray.Length = wholeArray.Length then areEqual subArray wholeArray else
        let rec loop subidx idx =
            if subidx = subArray.Length then true 
            elif subArray.[subidx] = wholeArray.[idx] then loop (subidx+1) (idx+1) 
            else false
        loop 0 index
        
    /// Returns true if one array has another as its subset from index 0.
    let startsWith (prefix: _ []) (whole: _ []) =
        isSubArray prefix whole 0
        
    /// Returns true if one array has trailing elements equal to another's.
    let endsWith (suffix: _ []) (whole: _ []) =
        isSubArray suffix whole (whole.Length-suffix.Length)
L
latkin 已提交
243 244
        
module Option = 
245

L
latkin 已提交
246 247
    let mapFold f s opt = 
        match opt with 
248
        | None -> None, s 
S
Steffen Forkmann 已提交
249
        | Some x -> 
250 251
            let x2, s2 = f s x 
            Some x2, s2
D
Don Syme 已提交
252

S
Steffen Forkmann 已提交
253 254
    let attempt (f: unit -> 'T) = try Some (f()) with _ -> None
        
L
latkin 已提交
255 256 257 258 259
module List = 

    let sortWithOrder (c: IComparer<'T>) elements = List.sortWith (Order.toFunction c) elements
    
    let splitAfter n l = 
D
Don Syme 已提交
260
        let rec split_after_acc n l1 l2 = if n <= 0 then List.rev l1, l2 else split_after_acc (n-1) ((List.head l2) :: l1) (List.tail l2) 
L
latkin 已提交
261 262 263
        split_after_acc n [] l

    let existsi f xs = 
D
Don Syme 已提交
264
       let rec loop i xs = match xs with [] -> false | h :: t -> f i h || loop (i+1) t
L
latkin 已提交
265 266 267 268 269 270 271 272 273
       loop 0 xs
    
    let lengthsEqAndForall2 p l1 l2 = 
        List.length l1 = List.length l2 &&
        List.forall2 p l1 l2

    let rec findi n f l = 
        match l with 
        | [] -> None
D
Don Syme 已提交
274
        | h :: t -> if f h then Some (h, n) else findi (n+1) f t
L
latkin 已提交
275 276 277

    let rec drop n l = 
        match l with 
278
        | [] -> []
D
Don Syme 已提交
279
        | _ :: xs -> if n=0 then l else drop (n-1) xs
L
latkin 已提交
280 281 282 283

    let splitChoose select l =
        let rec ch acc1 acc2 l = 
            match l with 
284
            | [] -> List.rev acc1, List.rev acc2
D
Don Syme 已提交
285
            | x :: xs -> 
L
latkin 已提交
286
                match select x with
D
Don Syme 已提交
287 288
                | Choice1Of2 sx -> ch (sx :: acc1) acc2 xs
                | Choice2Of2 sx -> ch acc1 (sx :: acc2) xs
L
latkin 已提交
289 290 291

        ch [] [] l

292
    let rec checkq l1 l2 = 
293
        match l1, l2 with 
D
Don Syme 已提交
294
        | h1 :: t1, h2 :: t2 -> h1 === h2 && checkq t1 t2
295 296
        | _ -> true

L
latkin 已提交
297 298 299 300
    let mapq (f: 'T -> 'T) inp =
        assert not (typeof<'T>.IsValueType) 
        match inp with
        | [] -> inp
301 302 303 304 305 306 307 308 309 310 311 312
        | [h1a] -> 
            let h2a = f h1a
            if h1a === h2a then inp else [h2a]
        | [h1a; h1b] -> 
            let h2a = f h1a
            let h2b = f h1b
            if h1a === h2a && h1b === h2b then inp else [h2a; h2b]
        | [h1a; h1b; h1c] -> 
            let h2a = f h1a
            let h2b = f h1b
            let h2c = f h1c
            if h1a === h2a && h1b === h2b && h1c === h2c then inp else [h2a; h2b; h2c]
L
latkin 已提交
313 314
        | _ -> 
            let res = List.map f inp 
315
            if checkq inp res then inp else res
L
latkin 已提交
316 317 318 319 320
        
    let frontAndBack l = 
        let rec loop acc l = 
            match l with
            | [] -> 
321
                Debug.Assert(false, "empty list")
L
latkin 已提交
322
                invalidArg "l" "empty list" 
323
            | [h] -> List.rev acc, h
D
Don Syme 已提交
324
            | h :: t -> loop (h :: acc) t
L
latkin 已提交
325 326 327 328 329 330
        loop [] l

    let tryRemove f inp = 
        let rec loop acc l = 
            match l with
            | [] -> None
D
Don Syme 已提交
331
            | h :: t -> if f h then Some (h, List.rev acc @ t) else loop (h :: acc) t
332
        loop [] inp
L
latkin 已提交
333 334

    let zip4 l1 l2 l3 l4 = 
335
        List.zip l1 (List.zip3 l2 l3 l4) |> List.map (fun (x1, (x2, x3, x4)) -> (x1, x2, x3, x4))
L
latkin 已提交
336 337

    let unzip4 l = 
338 339 340
        let a, b, cd = List.unzip3 (List.map (fun (x, y, z, w) -> (x, y, (z, w))) l)
        let c, d = List.unzip cd
        a, b, c, d
L
latkin 已提交
341 342

    let rec iter3 f l1 l2 l3 = 
343
        match l1, l2, l3 with 
D
Don Syme 已提交
344
        | h1 :: t1, h2 :: t2, h3 :: t3 -> f h1 h2 h3; iter3 f t1 t2 t3
L
latkin 已提交
345 346 347 348 349 350
        | [], [], [] -> ()
        | _ -> failwith "iter3"

    let takeUntil p l =
        let rec loop acc l =
            match l with
351
            | [] -> List.rev acc, []
D
Don Syme 已提交
352
            | x :: xs -> if p x then List.rev acc, l else loop (x :: acc) xs
L
latkin 已提交
353 354 355 356
        loop [] l

    let order (eltOrder: IComparer<'T>) =
        { new IComparer<list<'T>> with 
357
              member __.Compare(xs, ys) = 
L
latkin 已提交
358
                  let rec loop xs ys = 
359 360 361 362
                      match xs, ys with
                      | [], [] -> 0
                      | [], _ -> -1
                      | _, [] -> 1
D
Don Syme 已提交
363
                      | x :: xs, y :: ys -> 
364 365
                          let cxy = eltOrder.Compare(x, y)
                          if cxy=0 then loop xs ys else cxy 
L
latkin 已提交
366 367
                  loop xs ys }

368
    let indexNotFound() = raise (new KeyNotFoundException("An index satisfying the predicate was not found in the collection"))
L
latkin 已提交
369 370 371 372

    let rec assoc x l = 
        match l with 
        | [] -> indexNotFound()
D
Don Syme 已提交
373
        | ((h, r) :: t) -> if x = h then r else assoc x t
L
latkin 已提交
374 375 376 377

    let rec memAssoc x l = 
        match l with 
        | [] -> false
D
Don Syme 已提交
378
        | ((h, _) :: t) -> x = h || memAssoc x t
L
latkin 已提交
379 380 381 382

    let rec memq x l = 
        match l with 
        | [] -> false 
D
Don Syme 已提交
383
        | h :: t -> LanguagePrimitives.PhysicalEquality x h || memq x t
L
latkin 已提交
384 385 386 387

    let mapNth n f xs =
        let rec mn i = function
          | []    -> []
D
Don Syme 已提交
388
          | x :: xs -> if i=n then f x :: xs else x :: mn (i+1) xs
L
latkin 已提交
389 390 391 392 393 394 395 396
       
        mn 0 xs
    let count pred xs = List.fold (fun n x -> if pred x then n+1 else n) 0 xs

    // WARNING: not tail-recursive 
    let mapHeadTail fhead ftail = function
      | []    -> []
      | [x]   -> [fhead x]
D
Don Syme 已提交
397
      | x :: xs -> fhead x :: List.map ftail xs
L
latkin 已提交
398 399

    let collectFold f s l = 
400
      let l, s = List.mapFold f s l
L
latkin 已提交
401 402 403 404 405
      List.concat l, s

    let collect2 f xs ys = List.concat (List.map2 f xs ys)

    let toArraySquared xss = xss |> List.map List.toArray |> List.toArray
406

L
latkin 已提交
407
    let iterSquared f xss = xss |> List.iter (List.iter f)
408

L
latkin 已提交
409
    let collectSquared f xss = xss |> List.collect (List.collect f)
410

L
latkin 已提交
411
    let mapSquared f xss = xss |> List.map (List.map f)
412

413
    let mapFoldSquared f z xss = List.mapFold (List.mapFold f) z xss
414

L
latkin 已提交
415
    let forallSquared f xss = xss |> List.forall (List.forall f)
416

L
latkin 已提交
417
    let mapiSquared f xss = xss |> List.mapi (fun i xs -> xs |> List.mapi (fun j x -> f i j x))
418

L
latkin 已提交
419
    let existsSquared f xss = xss |> List.exists (fun xs -> xs |> List.exists (fun x -> f x))
420

421
    let mapiFoldSquared f z xss = mapFoldSquared f z (xss |> mapiSquared (fun i j x -> (i, j, x)))
L
latkin 已提交
422

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
module ResizeArray =

    /// Split a ResizeArray into an array of smaller chunks.
    /// This requires `items/chunkSize` Array copies of length `chunkSize` if `items/chunkSize % 0 = 0`,
    /// otherwise `items/chunkSize + 1` Array copies.
    let chunkBySize chunkSize f (items: ResizeArray<'t>) =
        // we could use Seq.chunkBySize here, but that would involve many enumerator.MoveNext() calls that we can sidestep with a bit of math
        let itemCount = items.Count
        if itemCount = 0
        then [||]
        else
            let chunksCount =
                match itemCount / chunkSize with
                | n when itemCount % chunkSize = 0 -> n
                | n -> n + 1 // any remainder means we need an additional chunk to store it

            [| for index in 0..chunksCount-1 do
                let startIndex = index * chunkSize
                let takeCount = min (itemCount - startIndex) chunkSize

                let holder = Array.zeroCreate takeCount
                // we take a bounds-check hit here on each access.
                // other alternatives here include
                // * iterating across an IEnumerator (incurs MoveNext penalty)
                // * doing a block copy using `List.CopyTo(index, array, index, count)` (requires more copies to do the mapping)
                // none are significantly better.
                for i in 0 .. takeCount - 1 do
                    holder.[i] <- f items.[i]
                yield holder |]

    /// Split a large ResizeArray into a series of array chunks that are each under the Large Object Heap limit.
    /// This is done to help prevent a stop-the-world collection of the single large array, instead allowing for a greater
    /// probability of smaller collections. Stop-the-world is still possible, just less likely.
    let mapToSmallArrayChunks f (inp: ResizeArray<'t>) =
        let itemSizeBytes = sizeof<'t>
        // rounding down here is good because it ensures we don't go over
        let maxArrayItemCount = LOH_SIZE_THRESHOLD_BYTES / itemSizeBytes

        /// chunk the provided input into arrays that are smaller than the LOH limit
        /// in order to prevent long-term storage of those values
        chunkBySize maxArrayItemCount f inp

465
module ValueOptionInternal =
466

467
    let inline ofOption x = match x with Some x -> ValueSome x | None -> ValueNone
468

469
    let inline bind f x = match x with ValueSome x -> f x | ValueNone -> ValueNone
470

471
type String with
D
Don Syme 已提交
472
    member inline x.StartsWithOrdinal value =
473 474
        x.StartsWith(value, StringComparison.Ordinal)

D
Don Syme 已提交
475
    member inline x.EndsWithOrdinal value =
476 477
        x.EndsWith(value, StringComparison.Ordinal)

478
module String =
479
    let make (n: int) (c: char) : string = new String(c, n)
L
latkin 已提交
480

481
    let get (str: string) i = str.[i]
L
latkin 已提交
482

483
    let sub (s: string) (start: int) (len: int) = s.Substring(start, len)
L
latkin 已提交
484

D
Don Syme 已提交
485
    let contains (s: string) (c: char) = s.IndexOf c <> -1
L
latkin 已提交
486 487 488

    let order = LanguagePrimitives.FastGenericComparer<string>
   
489
    let lowercase (s: string) =
L
latkin 已提交
490 491
        s.ToLowerInvariant()

492
    let uppercase (s: string) =
L
latkin 已提交
493 494
        s.ToUpperInvariant()

495 496 497 498 499 500 501 502 503 504 505 506 507 508
    // Scripts that distinguish between upper and lower case (bicameral) DU Discriminators and Active Pattern identifiers are required to start with an upper case character.
    // For valid identifiers where the case of the identifier can not be determined because there is no upper and lower case we will allow DU Discriminators and upper case characters 
    // to be used.  This means that developers using unicameral scripts such as hindi, are not required to prefix these identifiers with an Upper case latin character. 
    //
    let isLeadingIdentifierCharacterUpperCase (s:string) =
        let isUpperCaseCharacter c =
            // if IsUpper and IsLower return the same value, then we can't tell if it's upper or lower case, so ensure it is a letter
            // otherwise it is bicameral, so must be upper case
            let isUpper = Char.IsUpper c
            if isUpper = Char.IsLower c then Char.IsLetter c
            else isUpper

        s.Length >= 1 && isUpperCaseCharacter s.[0]

509
    let capitalize (s: string) =
L
latkin 已提交
510 511 512
        if s.Length = 0 then s 
        else uppercase s.[0..0] + s.[ 1.. s.Length - 1 ]

513 514
    let uncapitalize (s: string) =
        if s.Length = 0 then s
L
latkin 已提交
515 516
        else lowercase s.[0..0] + s.[ 1.. s.Length - 1 ]

517
    let dropPrefix (s: string) (t: string) = s.[t.Length..s.Length - 1]
L
latkin 已提交
518

519
    let dropSuffix (s: string) (t: string) = s.[0..s.Length - t.Length - 1]
L
latkin 已提交
520

521 522 523 524 525 526 527 528 529 530
    let inline toCharArray (str: string) = str.ToCharArray()

    let lowerCaseFirstChar (str: string) =
        if String.IsNullOrEmpty str 
         || Char.IsLower(str, 0) then str else 
        let strArr = toCharArray str
        match Array.tryHead strArr with
        | None -> str
        | Some c  -> 
            strArr.[0] <- Char.ToLower c
D
Don Syme 已提交
531
            String strArr
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551

    let extractTrailingIndex (str: string) =
        match str with
        | null -> null, None
        | _ ->
            let charr = str.ToCharArray() 
            Array.revInPlace charr
            let digits = Array.takeWhile Char.IsDigit charr
            Array.revInPlace digits
            String digits
            |> function
               | "" -> str, None
               | index -> str.Substring (0, str.Length - index.Length), Some (int index)

    /// Remove all trailing and leading whitespace from the string
    /// return null if the string is null
    let trim (value: string) = if isNull value then null else value.Trim()
    
    /// Splits a string into substrings based on the strings in the array separators
    let split options (separator: string []) (value: string) = 
552
        if isNull value then null else value.Split(separator, options)
553 554 555 556

    let (|StartsWith|_|) pattern value =
        if String.IsNullOrWhiteSpace value then
            None
D
Don Syme 已提交
557
        elif value.StartsWithOrdinal pattern then
558 559 560 561 562 563 564 565 566 567 568 569 570
            Some()
        else None

    let (|Contains|_|) pattern value =
        if String.IsNullOrWhiteSpace value then
            None
        elif value.Contains pattern then
            Some()
        else None

    let getLines (str: string) =
        use reader = new StringReader(str)
        [|
571 572 573 574
            let mutable line = reader.ReadLine()
            while not (isNull line) do
                yield line
                line <- reader.ReadLine()
D
Don Syme 已提交
575 576 577 578
            if str.EndsWithOrdinal("\n") then
                // last trailing space not returned
                // http://stackoverflow.com/questions/19365404/stringreader-omits-trailing-linebreak
                yield String.Empty
579
        |]
L
latkin 已提交
580

581
module Dictionary = 
582
    let inline newWithSize (size: int) = Dictionary<_, _>(size, HashIdentity.Structural)
583 584 585

[<Extension>]
type DictionaryExtensions() =
586

587 588 589 590 591 592 593 594 595 596 597
    [<Extension>]
    static member inline BagAdd(dic: Dictionary<'key, 'value list>, key: 'key, value: 'value) =
        match dic.TryGetValue key with
        | true, values -> dic.[key] <- value :: values
        | _ -> dic.[key] <- [value]

    [<Extension>]
    static member inline BagExistsValueForKey(dic: Dictionary<'key, 'value list>, key: 'key, f: 'value -> bool) =
        match dic.TryGetValue key with
        | true, values -> values |> List.exists f
        | _ -> false
598

L
latkin 已提交
599 600 601
module Lazy = 
    let force (x: Lazy<'T>) = x.Force()

602
//----------------------------------------------------------------------------
603
// Single threaded execution and mutual exclusion
604 605 606 607 608 609 610 611 612 613

/// Represents a permission active at this point in execution
type ExecutionToken = interface end

/// Represents a token that indicates execution on the compilation thread, i.e. 
///   - we have full access to the (partially mutable) TAST and TcImports data structures
///   - compiler execution may result in type provider invocations when resolving types and members
///   - we can access various caches in the SourceCodeServices
///
/// Like other execution tokens this should be passed via argument passing and not captured/stored beyond
614
/// the lifetime of stack-based calls. This is not checked, it is a discipline within the compiler code. 
615 616
type CompilationThreadToken() = interface ExecutionToken

617
/// Represents a place where we are stating that execution on the compilation thread is required. The
618 619 620
/// reason why will be documented in a comment in the code at the callsite.
let RequireCompilationThread (_ctok: CompilationThreadToken) = ()

621
/// Represents a place in the compiler codebase where we are passed a CompilationThreadToken unnecessarily.
E
Eugene Auduchinok 已提交
622
/// This represents code that may potentially not need to be executed on the compilation thread.
623
let DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent (_ctok: CompilationThreadToken) = ()
624

625
/// Represents a place in the compiler codebase where we assume we are executing on a compilation thread
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
let AssumeCompilationThreadWithoutEvidence () = Unchecked.defaultof<CompilationThreadToken>

/// Represents a token that indicates execution on a any of several potential user threads calling the F# compiler services.
type AnyCallerThreadToken() = interface ExecutionToken
let AssumeAnyCallerThreadWithoutEvidence () = Unchecked.defaultof<AnyCallerThreadToken>

/// A base type for various types of tokens that must be passed when a lock is taken.
/// Each different static lock should declare a new subtype of this type.
type LockToken = inherit ExecutionToken
let AssumeLockWithoutEvidence<'LockTokenType when 'LockTokenType :> LockToken> () = Unchecked.defaultof<'LockTokenType>

/// Encapsulates a lock associated with a particular token-type representing the acquisition of that lock.
type Lock<'LockTokenType when 'LockTokenType :> LockToken>() = 
    let lockObj = obj()
    member __.AcquireLock f = lock lockObj (fun () -> f (AssumeLockWithoutEvidence<'LockTokenType>()))

L
latkin 已提交
642 643 644 645 646 647 648 649 650 651 652
//---------------------------------------------------
// Misc

/// Get an initialization hole 
let getHole r = match !r with None -> failwith "getHole" | Some x -> x

module Map = 
    let tryFindMulti k map = match Map.tryFind k map with Some res -> res | None -> []

type ResultOrException<'TResult> =
    | Result of 'TResult
653
    | Exception of Exception
L
latkin 已提交
654
                     
D
Don Syme 已提交
655 656 657 658
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module ResultOrException = 

    let success a = Result a
659

660
    let raze (b: exn) = Exception b
D
Don Syme 已提交
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677

    // map
    let (|?>) res f = 
        match res with 
        | Result x -> Result(f x )
        | Exception err -> Exception err
  
    let ForceRaise res = 
        match res with 
        | Result x -> x
        | Exception err -> raise err

    let otherwise f x =
        match x with 
        | Result x -> success x
        | Exception _err -> f()

678 679 680 681 682 683 684 685
[<RequireQualifiedAccess>] 
type ValueOrCancelled<'TResult> =
    | Value of 'TResult
    | Cancelled of OperationCanceledException

/// Represents a cancellable computation with explicit representation of a cancelled result.
///
/// A cancellable computation is passed may be cancelled via a CancellationToken, which is propagated implicitly.  
686
/// If cancellation occurs, it is propagated as data rather than by raising an OperationCanceledException.  
687
type Cancellable<'TResult> = Cancellable of (CancellationToken -> ValueOrCancelled<'TResult>)
688 689 690 691

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Cancellable = 

D
Don Syme 已提交
692
    /// Run a cancellable computation using the given cancellation token
693
    let run (ct: CancellationToken) (Cancellable oper) = 
D
Don Syme 已提交
694
        if ct.IsCancellationRequested then 
695
            ValueOrCancelled.Cancelled (OperationCanceledException ct) 
D
Don Syme 已提交
696 697 698
        else
            oper ct 

699
    /// Bind the result of a cancellable computation
D
Don Syme 已提交
700
    let bind f comp1 = 
701
       Cancellable (fun ct -> 
D
Don Syme 已提交
702 703 704
            match run ct comp1 with 
            | ValueOrCancelled.Value v1 -> run ct (f v1) 
            | ValueOrCancelled.Cancelled err1 -> ValueOrCancelled.Cancelled err1)
705 706

    /// Map the result of a cancellable computation
D
Don Syme 已提交
707
    let map f oper = 
708
       Cancellable (fun ct -> 
D
Don Syme 已提交
709 710 711
           match run ct oper with 
           | ValueOrCancelled.Value res -> ValueOrCancelled.Value (f res)
           | ValueOrCancelled.Cancelled err -> ValueOrCancelled.Cancelled err)
712 713 714 715 716 717
                    
    /// Return a simple value as the result of a cancellable computation
    let ret x = Cancellable (fun _ -> ValueOrCancelled.Value x)

    /// Fold a cancellable computation along a sequence of inputs
    let fold f acc seq = 
D
Don Syme 已提交
718 719 720 721 722 723
        Cancellable (fun ct -> 
           (ValueOrCancelled.Value acc, seq) 
           ||> Seq.fold (fun acc x -> 
               match acc with 
               | ValueOrCancelled.Value accv -> run ct (f accv x)
               | res -> res))
724 725 726
    
    /// Iterate a cancellable computation over a collection
    let each f seq = 
D
Don Syme 已提交
727 728 729 730 731 732 733 734 735 736 737 738
        Cancellable (fun ct -> 
           (ValueOrCancelled.Value [], seq) 
           ||> Seq.fold (fun acc x -> 
               match acc with 
               | ValueOrCancelled.Value acc -> 
                   match run ct (f x) with 
                   | ValueOrCancelled.Value x2 -> ValueOrCancelled.Value (x2 :: acc)
                   | ValueOrCancelled.Cancelled err1 -> ValueOrCancelled.Cancelled err1
               | canc -> canc)
           |> function 
               | ValueOrCancelled.Value acc -> ValueOrCancelled.Value (List.rev acc)
               | canc -> canc)
739 740 741 742 743 744 745
    
    /// Delay a cancellable computation
    let delay (f: unit -> Cancellable<'T>) = Cancellable (fun ct -> let (Cancellable g) = f() in g ct)

    /// Run the computation in a mode where it may not be cancelled. The computation never results in a 
    /// ValueOrCancelled.Cancelled.
    let runWithoutCancellation comp = 
746
        let res = run CancellationToken.None comp 
747 748 749 750 751 752 753 754
        match res with 
        | ValueOrCancelled.Cancelled _ -> failwith "unexpected cancellation" 
        | ValueOrCancelled.Value r -> r

    /// Bind the cancellation token associated with the computation
    let token () = Cancellable (fun ct -> ValueOrCancelled.Value ct)

    /// Represents a canceled computation
755
    let canceled() = Cancellable (fun ct -> ValueOrCancelled.Cancelled (OperationCanceledException ct))
756 757 758 759 760 761 762 763 764 765 766 767

    /// Catch exceptions in a computation
    let private catch (Cancellable e) = 
        Cancellable (fun ct -> 
            try 
                match e ct with 
                | ValueOrCancelled.Value r -> ValueOrCancelled.Value (Choice1Of2 r) 
                | ValueOrCancelled.Cancelled e -> ValueOrCancelled.Cancelled e 
            with err -> 
                ValueOrCancelled.Value (Choice2Of2 err))

    /// Implement try/finally for a cancellable computation
768 769
    let tryFinally e compensation =
        catch e |> bind (fun res ->
770
            compensation()
771 772 773
            match res with Choice1Of2 r -> ret r | Choice2Of2 err -> raise err)

    /// Implement try/with for a cancellable computation
774 775
    let tryWith e handler = 
        catch e |> bind (fun res ->
776 777
            match res with Choice1Of2 r -> ret r | Choice2Of2 err -> handler err)
    
778
    // Run the cancellable computation within an Async computation. This isn't actually used in the codebase, but left
779 780
    // here in case we need it in the future 
    //
781
    // let toAsync e = 
782 783 784 785 786 787 788 789 790 791 792 793 794
    //     async { 
    //       let! ct = Async.CancellationToken
    //       return! 
    //          Async.FromContinuations(fun (cont, econt, ccont) -> 
    //            // Run the computation synchronously using the given cancellation token
    //            let res = try Choice1Of2 (run ct e) with err -> Choice2Of2 err
    //            match res with 
    //            | Choice1Of2 (ValueOrCancelled.Value v) -> cont v
    //            | Choice1Of2 (ValueOrCancelled.Cancelled err) -> ccont err
    //            | Choice2Of2 err -> econt err) 
    //     }
    
type CancellableBuilder() = 
795

796
    member x.Bind(e, k) = Cancellable.bind k e
797

D
Don Syme 已提交
798
    member x.Return v = Cancellable.ret v
799

D
Don Syme 已提交
800
    member x.ReturnFrom v = v
801

802
    member x.Combine(e1, e2) = e1 |> Cancellable.bind (fun () -> e2)
803

804
    member x.TryWith(e, handler) = Cancellable.tryWith e handler
805

806
    member x.Using(resource, e) = Cancellable.tryFinally (e resource) (fun () -> (resource :> IDisposable).Dispose())
807

808
    member x.TryFinally(e, compensation) =  Cancellable.tryFinally e compensation
809

D
Don Syme 已提交
810
    member x.Delay f = Cancellable.delay f
811

812 813 814 815
    member x.Zero() = Cancellable.ret ()

let cancellable = CancellableBuilder()

L
latkin 已提交
816 817 818 819 820 821 822 823 824
/// Computations that can cooperatively yield by returning a continuation
///
///    - Any yield of a NotYetDone should typically be "abandonable" without adverse consequences. No resource release
///      will be called when the computation is abandoned.
///
///    - Computations suspend via a NotYetDone may use local state (mutables), where these are
///      captured by the NotYetDone closure. Computations do not need to be restartable.
///
///    - The key thing is that you can take an Eventually value and run it with 
825
///      Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled
826 827
///
///    - Cancellation results in a suspended computation rather than complete abandonment
L
latkin 已提交
828 829
type Eventually<'T> = 
    | Done of 'T 
830
    | NotYetDone of (CompilationThreadToken -> Eventually<'T>)
L
latkin 已提交
831 832 833

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Eventually = 
834

L
latkin 已提交
835 836 837
    let rec box e = 
        match e with 
        | Done x -> Done (Operators.box x) 
D
Don Syme 已提交
838
        | NotYetDone work -> NotYetDone (fun ctok -> box (work ctok))
L
latkin 已提交
839

840
    let rec forceWhile ctok check e = 
L
latkin 已提交
841
        match e with 
D
Don Syme 已提交
842 843
        | Done x -> Some x
        | NotYetDone work -> 
L
latkin 已提交
844 845
            if not(check()) 
            then None
846
            else forceWhile ctok check (work ctok) 
L
latkin 已提交
847

848
    let force ctok e = Option.get (forceWhile ctok (fun () -> true) e)
L
latkin 已提交
849
        
D
Don Syme 已提交
850
    /// Keep running the computation bit by bit until a time limit is reached.
L
latkin 已提交
851
    /// The runner gets called each time the computation is restarted
852 853
    ///
    /// If cancellation happens, the operation is left half-complete, ready to resume.
854
    let repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled timeShareInMilliseconds (ct: CancellationToken) runner e = 
855
        let sw = new Stopwatch() 
856 857
        let rec runTimeShare ctok e = 
          runner ctok (fun ctok -> 
L
latkin 已提交
858
            sw.Reset()
859
            sw.Start()
860 861 862
            let rec loop ctok ev2 = 
                match ev2 with 
                | Done _ -> ev2
863 864
                | NotYetDone work ->
                    if ct.IsCancellationRequested || sw.ElapsedMilliseconds > timeShareInMilliseconds then 
865
                        sw.Stop()
866
                        NotYetDone(fun ctok -> runTimeShare ctok ev2) 
L
latkin 已提交
867
                    else 
868 869 870
                        loop ctok (work ctok)
            loop ctok e)
        NotYetDone (fun ctok -> runTimeShare ctok e)
871 872
    
    /// Keep running the asynchronous computation bit by bit. The runner gets called each time the computation is restarted.
873
    /// Can be cancelled as an Async in the normal way.
874
    let forceAsync (runner: (CompilationThreadToken -> Eventually<'T>) -> Async<Eventually<'T>>) (e: Eventually<'T>) : Async<'T option> =
875 876 877 878 879 880 881 882 883
        let rec loop (e: Eventually<'T>) =
            async {
                match e with 
                | Done x -> return Some x
                | NotYetDone work ->
                    let! r = runner work
                    return! loop r
            }
        loop e
L
latkin 已提交
884 885 886 887

    let rec bind k e = 
        match e with 
        | Done x -> k x 
888
        | NotYetDone work -> NotYetDone (fun ctok -> bind k (work ctok))
L
latkin 已提交
889 890

    let fold f acc seq = 
891
        (Done acc, seq) ||> Seq.fold (fun acc x -> acc |> bind (fun acc -> f acc x))
L
latkin 已提交
892 893 894 895 896
        
    let rec catch e = 
        match e with 
        | Done x -> Done(Result x)
        | NotYetDone work -> 
897 898
            NotYetDone (fun ctok -> 
                let res = try Result(work ctok) with | e -> Exception e 
L
latkin 已提交
899 900 901 902
                match res with 
                | Result cont -> catch cont
                | Exception e -> Done(Exception e))
    
903
    let delay (f: unit -> Eventually<'T>) = NotYetDone (fun _ctok -> f())
L
latkin 已提交
904

905
    let tryFinally e compensation =
D
Don Syme 已提交
906
        catch e 
907 908 909 910 911
        |> bind (fun res -> 
            compensation()
            match res with 
            | Result v -> Eventually.Done v
            | Exception e -> raise e)
L
latkin 已提交
912

913
    let tryWith e handler =
L
latkin 已提交
914 915 916
        catch e 
        |> bind (function Result v -> Done v | Exception e -> handler e)
    
917
    // All eventually computations carry a CompilationThreadToken
918 919 920
    let token =    
        NotYetDone (fun ctok -> Done ctok)
    
L
latkin 已提交
921
type EventuallyBuilder() = 
922

923
    member x.Bind(e, k) = Eventually.bind k e
924

D
Don Syme 已提交
925
    member x.Return v = Eventually.Done v
926

D
Don Syme 已提交
927
    member x.ReturnFrom v = v
928

929
    member x.Combine(e1, e2) = e1 |> Eventually.bind (fun () -> e2)
930

931
    member x.TryWith(e, handler) = Eventually.tryWith e handler
932

933
    member x.TryFinally(e, compensation) = Eventually.tryFinally e compensation
934

D
Don Syme 已提交
935
    member x.Delay f = Eventually.delay f
L
latkin 已提交
936

937
    member x.Zero() = Eventually.Done ()
L
latkin 已提交
938 939 940 941 942 943 944 945 946 947 948

let eventually = new EventuallyBuilder()

(*
let _ = eventually { return 1 }
let _ = eventually { let x = 1 in return 1 }
let _ = eventually { let! x = eventually { return 1 } in return 1 }
let _ = eventually { try return (failwith "") with _ -> return 1 }
let _ = eventually { use x = null in return 1 }
*)

949
/// Generates unique stamps
L
latkin 已提交
950
type UniqueStampGenerator<'T when 'T : equality>() = 
951
    let encodeTab = new Dictionary<'T, int>(HashIdentity.Structural)
L
latkin 已提交
952
    let mutable nItems = 0
953
    let encode str =
D
Don Syme 已提交
954
        match encodeTab.TryGetValue str with
955 956
        | true, idx -> idx
        | _ ->
L
latkin 已提交
957 958 959 960
            let idx = nItems
            encodeTab.[str] <- idx
            nItems <- nItems + 1
            idx
961

D
Don Syme 已提交
962
    member this.Encode str = encode str
963

964
    member this.Table = encodeTab.Keys
L
latkin 已提交
965

966
/// memoize tables (all entries cached, never collected)
967
type MemoizationTable<'T, 'U>(compute: 'T -> 'U, keyComparer: IEqualityComparer<'T>, ?canMemoize) = 
L
latkin 已提交
968
    
969
    let table = new Dictionary<'T, 'U>(keyComparer) 
970

D
Don Syme 已提交
971
    member t.Apply x = 
L
latkin 已提交
972
        if (match canMemoize with None -> true | Some f -> f x) then 
N
ncave 已提交
973 974 975
            match table.TryGetValue x with
            | true, res -> res
            | _ ->
L
latkin 已提交
976
                lock table (fun () -> 
N
ncave 已提交
977 978 979
                    match table.TryGetValue x with
                    | true, res -> res
                    | _ ->
L
latkin 已提交
980
                        let res = compute x
981
                        table.[x] <- res
L
latkin 已提交
982 983 984 985 986 987
                        res)
        else compute x


exception UndefinedException

988
type LazyWithContextFailure(exn: exn) =
989

L
latkin 已提交
990
    static let undefined = new LazyWithContextFailure(UndefinedException)
991

L
latkin 已提交
992
    member x.Exception = exn
993

L
latkin 已提交
994 995 996 997 998 999
    static member Undefined = undefined
        
/// Just like "Lazy" but EVERY forcer must provide an instance of "ctxt", e.g. to help track errors
/// on forcing back to at least one sensible user location
[<DefaultAugmentation(false)>]
[<NoEquality; NoComparison>]
1000
type LazyWithContext<'T, 'ctxt> = 
L
latkin 已提交
1001 1002
    { /// This field holds the result of a successful computation. It's initial value is Unchecked.defaultof
      mutable value : 'T
1003

L
latkin 已提交
1004 1005
      /// This field holds either the function to run or a LazyWithContextFailure object recording the exception raised 
      /// from running the function. It is null if the thunk has been evaluated successfully.
1006 1007
      mutable funcOrException: obj

L
latkin 已提交
1008 1009
      /// A helper to ensure we rethrow the "original" exception
      findOriginalException : exn -> exn }
1010

1011
    static member Create(f: ('ctxt->'T), findOriginalException) : LazyWithContext<'T, 'ctxt> = 
1012 1013
        { value = Unchecked.defaultof<'T>
          funcOrException = box f
L
latkin 已提交
1014
          findOriginalException = findOriginalException }
1015

1016
    static member NotLazy(x:'T) : LazyWithContext<'T, 'ctxt> = 
S
Steffen Forkmann 已提交
1017 1018
        { value = x
          funcOrException = null
L
latkin 已提交
1019
          findOriginalException = id }
1020

L
latkin 已提交
1021
    member x.IsDelayed = (match x.funcOrException with null -> false | :? LazyWithContextFailure -> false | _ -> true)
1022

L
latkin 已提交
1023
    member x.IsForced = (match x.funcOrException with null -> true | _ -> false)
1024

1025
    member x.Force(ctxt:'ctxt) = 
L
latkin 已提交
1026 1027 1028
        match x.funcOrException with 
        | null -> x.value 
        | _ -> 
D
dungpa 已提交
1029
            // Enter the lock in case another thread is in the process of evaluating the result
D
Don Syme 已提交
1030
            Monitor.Enter x;
L
latkin 已提交
1031
            try 
D
Don Syme 已提交
1032
                x.UnsynchronizedForce ctxt
L
latkin 已提交
1033
            finally
D
Don Syme 已提交
1034
                Monitor.Exit x
L
latkin 已提交
1035

D
Don Syme 已提交
1036
    member x.UnsynchronizedForce ctxt = 
L
latkin 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045
        match x.funcOrException with 
        | null -> x.value 
        | :? LazyWithContextFailure as res -> 
              // Re-raise the original exception 
              raise (x.findOriginalException res.Exception)
        | :? ('ctxt -> 'T) as f -> 
              x.funcOrException <- box(LazyWithContextFailure.Undefined)
              try 
                  let res = f ctxt 
1046 1047
                  x.value <- res
                  x.funcOrException <- null
L
latkin 已提交
1048 1049
                  res
              with e -> 
1050
                  x.funcOrException <- box(new LazyWithContextFailure(e))
L
latkin 已提交
1051 1052 1053 1054
                  reraise()
        | _ -> 
            failwith "unreachable"

1055
/// Intern tables to save space.
L
latkin 已提交
1056 1057
module Tables = 
    let memoize f = 
1058
        let t = new Dictionary<_, _>(1000, HashIdentity.Structural)
L
latkin 已提交
1059
        fun x -> 
N
ncave 已提交
1060 1061 1062 1063
            match t.TryGetValue x with
            | true, res -> res
            | _ ->
                let res = f x
1064 1065
                t.[x] <- res
                res
L
latkin 已提交
1066

D
Don Syme 已提交
1067 1068 1069 1070 1071 1072 1073
/// Interface that defines methods for comparing objects using partial equality relation
type IPartialEqualityComparer<'T> = 
    inherit IEqualityComparer<'T>
    /// Can the specified object be tested for equality?
    abstract InEqualityRelation : 'T -> bool

module IPartialEqualityComparer = 
1074

D
Don Syme 已提交
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
    let On f (c: IPartialEqualityComparer<_>) = 
          { new IPartialEqualityComparer<_> with 
                member __.InEqualityRelation x = c.InEqualityRelation (f x)
                member __.Equals(x, y) = c.Equals(f x, f y)
                member __.GetHashCode x = c.GetHashCode(f x) }
    
    // Wrapper type for use by the 'partialDistinctBy' function
    [<StructuralEquality; NoComparison>]
    type private WrapType<'T> = Wrap of 'T
    
    // Like Seq.distinctBy but only filters out duplicates for some of the elements
1086
    let partialDistinctBy (per: IPartialEqualityComparer<'T>) seq =
D
Don Syme 已提交
1087 1088
        let wper = 
            { new IPartialEqualityComparer<WrapType<'T>> with
D
Don Syme 已提交
1089
                member __.InEqualityRelation (Wrap x) = per.InEqualityRelation x
D
Don Syme 已提交
1090
                member __.Equals(Wrap x, Wrap y) = per.Equals(x, y)
D
Don Syme 已提交
1091
                member __.GetHashCode (Wrap x) = per.GetHashCode x }
D
Don Syme 已提交
1092
        // Wrap a Wrap _ around all keys in case the key type is itself a type using null as a representation
1093
        let dict = Dictionary<WrapType<'T>, obj>(wper)
D
Don Syme 已提交
1094
        seq |> List.filter (fun v -> 
D
Don Syme 已提交
1095 1096 1097
            let key = Wrap v
            if (per.InEqualityRelation v) then 
                if dict.ContainsKey key then false else (dict.[key] <- null; true)
D
Don Syme 已提交
1098 1099
            else true)

L
latkin 已提交
1100 1101 1102 1103
//-------------------------------------------------------------------------
// Library: Name maps
//------------------------------------------------------------------------

1104
type NameMap<'T> = Map<string, 'T>
1105

L
latkin 已提交
1106
type NameMultiMap<'T> = NameMap<'T list>
1107

1108
type MultiMap<'T, 'U when 'T : comparison> = Map<'T, 'U list>
L
latkin 已提交
1109 1110 1111 1112 1113

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module NameMap = 

    let empty = Map.empty
1114

L
latkin 已提交
1115
    let range m = List.rev (Map.foldBack (fun _ x sofar -> x :: sofar) m [])
1116

1117
    let foldBack f (m: NameMap<'T>) z = Map.foldBack f m z
1118

L
latkin 已提交
1119
    let forall f m = Map.foldBack (fun x y sofar -> sofar && f x y) m true
1120

L
latkin 已提交
1121
    let exists f m = Map.foldBack (fun x y sofar -> sofar || f x y) m false
1122

L
latkin 已提交
1123
    let ofKeyedList f l = List.foldBack (fun x acc -> Map.add (f x) x acc) l Map.empty
1124

L
latkin 已提交
1125
    let ofList l : NameMap<'T> = Map.ofList l
1126

S
Steffen Forkmann 已提交
1127
    let ofSeq l : NameMap<'T> = Map.ofSeq l
1128

L
latkin 已提交
1129
    let toList (l: NameMap<'T>) = Map.toList l
1130

L
latkin 已提交
1131 1132 1133 1134 1135 1136 1137 1138 1139
    let layer (m1 : NameMap<'T>) m2 = Map.foldBack Map.add m1 m2

    /// Not a very useful function - only called in one place - should be changed 
    let layerAdditive addf m1 m2 = 
      Map.foldBack (fun x y sofar -> Map.add x (addf (Map.tryFindMulti x sofar) y) sofar) m1 m2

    /// Union entries by identical key, using the provided function to union sets of values
    let union unionf (ms: NameMap<_> seq) = 
        seq { for m in ms do yield! m } 
1140 1141
           |> Seq.groupBy (fun (KeyValue(k, _v)) -> k) 
           |> Seq.map (fun (k, es) -> (k, unionf (Seq.map (fun (KeyValue(_k, v)) -> v) es))) 
L
latkin 已提交
1142 1143 1144 1145 1146 1147 1148 1149 1150
           |> Map.ofSeq

    /// For every entry in m2 find an entry in m1 and fold 
    let subfold2 errf f m1 m2 acc =
        Map.foldBack (fun n x2 acc -> try f n (Map.find n m1) x2 acc with :? KeyNotFoundException -> errf n x2) m2 acc

    let suball2 errf p m1 m2 = subfold2 errf (fun _ x1 x2 acc -> p x1 x2 && acc) m1 m2 true

    let mapFold f s (l: NameMap<'T>) = 
1151
        Map.foldBack (fun x y (l2, sx) -> let y2, sy = f sx x y in Map.add x y2 l2, sy) l (Map.empty, s)
L
latkin 已提交
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172

    let foldBackRange f (l: NameMap<'T>) acc = Map.foldBack (fun _ y acc -> f y acc) l acc

    let filterRange f (l: NameMap<'T>) = Map.foldBack (fun x y acc -> if f y then Map.add x y acc else acc) l Map.empty

    let mapFilter f (l: NameMap<'T>) = Map.foldBack (fun x y acc -> match f y with None -> acc | Some y' -> Map.add x y' acc) l Map.empty

    let map f (l : NameMap<'T>) = Map.map (fun _ x -> f x) l

    let iter f (l : NameMap<'T>) = Map.iter (fun _k v -> f v) l

    let partition f (l : NameMap<'T>) = Map.filter (fun _ x-> f x) l, Map.filter (fun _ x -> not (f x)) l

    let mem v (m: NameMap<'T>) = Map.containsKey v m

    let find v (m: NameMap<'T>) = Map.find v m

    let tryFind v (m: NameMap<'T>) = Map.tryFind v m 

    let add v x (m: NameMap<'T>) = Map.add v x m

1173
    let isEmpty (m: NameMap<'T>) = (Map.isEmpty m)
L
latkin 已提交
1174

1175
    let existsInRange p m = Map.foldBack (fun _ y acc -> acc || p y) m false 
L
latkin 已提交
1176 1177 1178 1179 1180 1181 1182 1183 1184

    let tryFindInRange p m = 
        Map.foldBack (fun _ y acc -> 
             match acc with 
             | None -> if p y then Some y else None 
             | _ -> acc) m None 

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module NameMultiMap = 
1185

L
latkin 已提交
1186
    let existsInRange f (m: NameMultiMap<'T>) = NameMap.exists (fun _ l -> List.exists f l) m
1187

1188
    let find v (m: NameMultiMap<'T>) = match m.TryGetValue v with true, r -> r | _ -> []
1189

L
latkin 已提交
1190
    let add v x (m: NameMultiMap<'T>) = NameMap.add v (x :: find v m) m
1191

L
latkin 已提交
1192
    let range (m: NameMultiMap<'T>) = Map.foldBack (fun _ x sofar -> x @ sofar) m []
1193

L
latkin 已提交
1194 1195 1196
    let rangeReversingEachBucket (m: NameMultiMap<'T>) = Map.foldBack (fun _ x sofar -> List.rev x @ sofar) m []
    
    let chooseRange f (m: NameMultiMap<'T>) = Map.foldBack (fun _ x sofar -> List.choose f x @ sofar) m []
1197

L
latkin 已提交
1198
    let map f (m: NameMultiMap<'T>) = NameMap.map (List.map f) m 
1199

L
latkin 已提交
1200
    let empty : NameMultiMap<'T> = Map.empty
1201

1202
    let initBy f xs : NameMultiMap<'T> = xs |> Seq.groupBy f |> Seq.map (fun (k, v) -> (k, List.ofSeq v)) |> Map.ofSeq 
1203

1204
    let ofList (xs: (string * 'T) list) : NameMultiMap<'T> = xs |> Seq.groupBy fst |> Seq.map (fun (k, v) -> (k, List.ofSeq (Seq.map snd v))) |> Map.ofSeq 
L
latkin 已提交
1205 1206 1207

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MultiMap = 
1208

1209
    let existsInRange f (m: MultiMap<_, _>) = Map.exists (fun _ l -> List.exists f l) m
1210

1211
    let find v (m: MultiMap<_, _>) = match m.TryGetValue v with true, r -> r | _ -> []
1212

1213
    let add v x (m: MultiMap<_, _>) = Map.add v (x :: find v m) m
1214

1215
    let range (m: MultiMap<_, _>) = Map.foldBack (fun _ x sofar -> x @ sofar) m []
1216

1217
    let empty : MultiMap<_, _> = Map.empty
1218

1219
    let initBy f xs : MultiMap<_, _> = xs |> Seq.groupBy f |> Seq.map (fun (k, v) -> (k, List.ofSeq v)) |> Map.ofSeq 
L
latkin 已提交
1220

1221
type LayeredMap<'Key, 'Value when 'Key : comparison> = Map<'Key, 'Value>
L
latkin 已提交
1222

1223
type Map<'Key, 'Value when 'Key : comparison> with
1224

1225
    static member Empty : Map<'Key, 'Value> = Map.empty
L
latkin 已提交
1226

1227
    member x.Values = [ for (KeyValue(_, v)) in x -> v ]
1228

1229
    member x.AddAndMarkAsCollapsible (kvs: _[]) = (x, kvs) ||> Array.fold (fun x (KeyValue(k, v)) -> x.Add(k, v))
1230

L
latkin 已提交
1231
    member x.LinearTryModifyThenLaterFlatten (key, f: 'Value option -> 'Value) = x.Add (key, f (x.TryFind key))
1232

1233
    member x.MarkAsCollapsible () = x
L
latkin 已提交
1234 1235 1236

/// Immutable map collection, with explicit flattening to a backing dictionary 
[<Sealed>]
1237
type LayeredMultiMap<'Key, 'Value when 'Key : equality and 'Key : comparison>(contents : LayeredMap<'Key, 'Value list>) = 
1238

1239
    member x.Add (k, v) = LayeredMultiMap(contents.Add(k, v :: x.[k]))
1240

1241
    member x.Item with get k = match contents.TryGetValue k with true, l -> l | _ -> []
1242

1243 1244
    member x.AddAndMarkAsCollapsible (kvs: _[]) = 
        let x = (x, kvs) ||> Array.fold (fun x (KeyValue(k, v)) -> x.Add(k, v))
L
latkin 已提交
1245
        x.MarkAsCollapsible()
1246

L
latkin 已提交
1247
    member x.MarkAsCollapsible() = LayeredMultiMap(contents.MarkAsCollapsible())
1248

L
latkin 已提交
1249
    member x.TryFind k = contents.TryFind k
1250

1251
    member x.TryGetValue k = contents.TryGetValue k
1252

1253
    member x.Values = contents.Values |> List.concat
1254

1255
    static member Empty : LayeredMultiMap<'Key, 'Value> = LayeredMultiMap LayeredMap.Empty
L
latkin 已提交
1256 1257 1258 1259

[<AutoOpen>]
module Shim =

D
Don Syme 已提交
1260
    type IFileSystem = 
1261 1262

        /// A shim over File.ReadAllBytes
1263
        abstract ReadAllBytesShim: fileName: string -> byte[] 
1264

1265 1266
        /// A shim over FileStream with FileMode.Open, FileAccess.Read, FileShare.ReadWrite
        abstract FileStreamReadShim: fileName: string -> Stream
1267

1268 1269
        /// A shim over FileStream with FileMode.Create, FileAccess.Write, FileShare.Read
        abstract FileStreamCreateShim: fileName: string -> Stream
1270

1271 1272
        /// A shim over FileStream with FileMode.Open, FileAccess.Write, FileShare.Read
        abstract FileStreamWriteExistingShim: fileName: string -> Stream
1273

L
latkin 已提交
1274 1275 1276
        /// Take in a filename with an absolute path, and return the same filename
        /// but canonicalized with respect to extra path separators (e.g. C:\\\\foo.txt) 
        /// and '..' portions
1277
        abstract GetFullPathShim: fileName: string -> string
1278 1279

        /// A shim over Path.IsPathRooted
1280
        abstract IsPathRootedShim: path: string -> bool
1281 1282

        /// A shim over Path.IsInvalidPath
1283
        abstract IsInvalidPathShim: filename: string -> bool
1284 1285

        /// A shim over Path.GetTempPath
L
latkin 已提交
1286
        abstract GetTempPathShim : unit -> string
1287 1288

        /// Utc time of the last modification
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
        abstract GetLastWriteTimeShim: fileName: string -> DateTime

        /// A shim over File.Exists
        abstract SafeExists: fileName: string -> bool

        /// A shim over File.Delete
        abstract FileDelete: fileName: string -> unit

        /// Used to load type providers and located assemblies in F# Interactive
        abstract AssemblyLoadFrom: fileName: string -> Assembly 

        /// Used to load a dependency for F# Interactive and in an unused corner-case of type provider loading
        abstract AssemblyLoad: assemblyName: AssemblyName -> Assembly 

        /// Used to determine if a file will not be subject to deletion during the lifetime of a typical client process.
        abstract IsStableFileHeuristic: fileName: string -> bool
L
latkin 已提交
1305

1306

D
Don Syme 已提交
1307 1308
    type DefaultFileSystem() =
        interface IFileSystem with
1309 1310

            member __.AssemblyLoadFrom(fileName: string) = 
K
Kevin Ransom (msft) 已提交
1311
                Assembly.UnsafeLoadFrom fileName
1312 1313

            member __.AssemblyLoad(assemblyName: AssemblyName) = 
D
Don Syme 已提交
1314
                Assembly.Load assemblyName
L
latkin 已提交
1315

1316 1317
            member __.ReadAllBytesShim (fileName: string) = File.ReadAllBytes fileName

1318
            member __.FileStreamReadShim (fileName: string) = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)  :> Stream
1319

D
Don Syme 已提交
1320
            member __.FileStreamCreateShim (fileName: string) = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read, 0x1000, false) :> Stream
L
latkin 已提交
1321

D
Don Syme 已提交
1322
            member __.FileStreamWriteExistingShim (fileName: string) = new FileStream(fileName, FileMode.Open, FileAccess.Write, FileShare.Read, 0x1000, false) :> Stream
L
latkin 已提交
1323

1324 1325 1326 1327 1328
            member __.GetFullPathShim (fileName: string) = System.IO.Path.GetFullPath fileName

            member __.IsPathRootedShim (path: string) = Path.IsPathRooted path

            member __.IsInvalidPathShim(path: string) = 
1329
                let isInvalidPath(p: string) = 
D
Don Syme 已提交
1330
                    String.IsNullOrEmpty p || p.IndexOfAny(Path.GetInvalidPathChars()) <> -1
D
Don Syme 已提交
1331

1332
                let isInvalidFilename(p: string) = 
D
Don Syme 已提交
1333
                    String.IsNullOrEmpty p || p.IndexOfAny(Path.GetInvalidFileNameChars()) <> -1
K
KevinRansom 已提交
1334

1335
                let isInvalidDirectory(d: string) = 
D
Don Syme 已提交
1336 1337
                    d=null || d.IndexOfAny(Path.GetInvalidPathChars()) <> -1

D
Don Syme 已提交
1338 1339 1340 1341
                isInvalidPath path || 
                let directory = Path.GetDirectoryName path
                let filename = Path.GetFileName path
                isInvalidDirectory directory || isInvalidFilename filename
L
latkin 已提交
1342

1343
            member __.GetTempPathShim() = Path.GetTempPath()
L
latkin 已提交
1344

1345
            member __.GetLastWriteTimeShim (fileName: string) = File.GetLastWriteTimeUtc fileName
L
latkin 已提交
1346

1347
            member __.SafeExists (fileName: string) = File.Exists fileName 
1348

1349
            member __.FileDelete (fileName: string) = File.Delete fileName
1350 1351

            member __.IsStableFileHeuristic (fileName: string) = 
D
Don Syme 已提交
1352
                let directory = Path.GetDirectoryName fileName
1353 1354 1355 1356 1357
                directory.Contains("Reference Assemblies/") || 
                directory.Contains("Reference Assemblies\\") || 
                directory.Contains("packages/") || 
                directory.Contains("packages\\") || 
                directory.Contains("lib/mono/")
L
latkin 已提交
1358

1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
    let mutable FileSystem = DefaultFileSystem() :> IFileSystem

    // The choice of 60 retries times 50 ms is not arbitrary. The NTFS FILETIME structure 
    // uses 2 second resolution for LastWriteTime. We retry long enough to surpass this threshold 
    // plus 1 second. Once past the threshold the incremental builder will be able to retry asynchronously based
    // on plain old timestamp checking.
    //
    // The sleep time of 50ms is chosen so that we can respond to the user more quickly for Intellisense operations.
    //
    // This is not run on the UI thread for VS but it is on a thread that must be stopped before Intellisense
    // can return any result except for pending.
    let private retryDelayMilliseconds = 50
    let private numRetries = 60

    let private getReader (filename, codePage: int option, retryLocked: bool) =
        // Retry multiple times since other processes may be writing to this file.
        let rec getSource retryNumber =
          try 
            // Use the .NET functionality to auto-detect the unicode encoding
            let stream = FileSystem.FileStreamReadShim(filename) 
            match codePage with 
            | None -> new  StreamReader(stream,true)
            | Some n -> new  StreamReader(stream,System.Text.Encoding.GetEncoding(n))
          with 
              // We can get here if the file is locked--like when VS is saving a file--we don't have direct
              // access to the HRESULT to see that this is EONOACCESS.
              | :? System.IO.IOException as err when retryLocked && err.GetType() = typeof<System.IO.IOException> -> 
                   // This second check is to make sure the exception is exactly IOException and none of these for example:
                   //   DirectoryNotFoundException 
                   //   EndOfStreamException 
                   //   FileNotFoundException 
                   //   FileLoadException 
                   //   PathTooLongException
                   if retryNumber < numRetries then 
                       System.Threading.Thread.Sleep (retryDelayMilliseconds)
                       getSource (retryNumber + 1)
                   else 
                       reraise()
        getSource 0
1398 1399

    type File with 
1400

1401 1402 1403
        static member ReadBinaryChunk (fileName, start, len) = 
            use stream = FileSystem.FileStreamReadShim fileName
            stream.Seek(int64 start, SeekOrigin.Begin) |> ignore
1404
            let buffer = Array.zeroCreate len 
1405 1406 1407 1408 1409
            let mutable n = 0
            while n < len do 
                n <- n + stream.Read(buffer, n, len-n)
            buffer

1410 1411 1412
        static member OpenReaderAndRetry (filename, codepage, retryLocked)  =
            getReader (filename, codepage, retryLocked)