diff --git a/src/fsharp/FSharp.Core/FSharp.Core.fsproj b/src/fsharp/FSharp.Core/FSharp.Core.fsproj index 8ad25ce16c7..d3d795c8bd0 100644 --- a/src/fsharp/FSharp.Core/FSharp.Core.fsproj +++ b/src/fsharp/FSharp.Core/FSharp.Core.fsproj @@ -95,6 +95,12 @@ Collections/collections.fs + + Collections/seqcore.fsi + + + Collections/seqcore.fs + Collections/seq.fsi diff --git a/src/fsharp/FSharp.Core/seq.fs b/src/fsharp/FSharp.Core/seq.fs index ca062b62d2a..eb60fe3d612 100644 --- a/src/fsharp/FSharp.Core/seq.fs +++ b/src/fsharp/FSharp.Core/seq.fs @@ -13,49 +13,9 @@ namespace Microsoft.FSharp.Collections open Microsoft.FSharp.Control open Microsoft.FSharp.Collections - module IEnumerator = - - - let noReset() = raise (new System.NotSupportedException(SR.GetString(SR.resetNotSupported))) - let notStarted() = raise (new System.InvalidOperationException(SR.GetString(SR.enumerationNotStarted))) - let alreadyFinished() = raise (new System.InvalidOperationException(SR.GetString(SR.enumerationAlreadyFinished))) - let check started = if not started then notStarted() - let dispose (r : System.IDisposable) = r.Dispose() - - let cast (e : IEnumerator) : IEnumerator<'T> = - { new IEnumerator<'T> with - member x.Current = unbox<'T> e.Current - interface IEnumerator with - member x.Current = unbox<'T> e.Current :> obj - member x.MoveNext() = e.MoveNext() - member x.Reset() = noReset() - interface System.IDisposable with - member x.Dispose() = - match e with - | :? System.IDisposable as e -> e.Dispose() - | _ -> () } - - /// A concrete implementation of an enumerator that returns no values - [] - type EmptyEnumerator<'T>() = - let mutable started = false - interface IEnumerator<'T> with - member x.Current = - check started - (alreadyFinished() : 'T) - - interface System.Collections.IEnumerator with - member x.Current = - check started - (alreadyFinished() : obj) - member x.MoveNext() = - if not started then started <- true - false - member x.Reset() = noReset() - interface System.IDisposable with - member x.Dispose() = () - - let Empty<'T> () = (new EmptyEnumerator<'T>() :> IEnumerator<'T>) + module Internal = + module IEnumerator = + open Microsoft.FSharp.Collections.IEnumerator let rec tryItem index (e : IEnumerator<'T>) = if not (e.MoveNext()) then None @@ -297,37 +257,6 @@ namespace Microsoft.FSharp.Collections interface System.IDisposable with member x.Dispose() = () } - let readAndClear r = - lock r (fun () -> match !r with None -> None | Some _ as res -> r := None; res) - - let generateWhileSome openf compute closef : IEnumerator<'U> = - let started = ref false - let curr = ref None - let state = ref (Some(openf())) - let getCurr() = - check !started - match !curr with None -> alreadyFinished() | Some x -> x - let start() = if not !started then (started := true) - - let dispose() = readAndClear state |> Option.iter closef - let finish() = (try dispose() finally curr := None) - { new IEnumerator<'U> with - member x.Current = getCurr() - interface IEnumerator with - member x.Current = box (getCurr()) - member x.MoveNext() = - start() - match !state with - | None -> false (* we started, then reached the end, then got another MoveNext *) - | Some s -> - match (try compute s with e -> finish(); reraise()) with - | None -> finish(); false - | Some _ as x -> curr := x; true - - member x.Reset() = noReset() - interface System.IDisposable with - member x.Dispose() = dispose() } - [] type ArrayEnumerator<'T>(arr: 'T array) = let mutable curr = -1 @@ -353,35 +282,6 @@ namespace Microsoft.FSharp.Collections let ofArray arr = (new ArrayEnumerator<'T>(arr) :> IEnumerator<'T>) - [] - type Singleton<'T>(v:'T) = - let mutable started = false - interface IEnumerator<'T> with - member x.Current = v - interface IEnumerator with - member x.Current = box v - member x.MoveNext() = if started then false else (started <- true; true) - member x.Reset() = noReset() - interface System.IDisposable with - member x.Dispose() = () - - let Singleton x = (new Singleton<'T>(x) :> IEnumerator<'T>) - - let EnumerateThenFinally f (e : IEnumerator<'T>) = - { new IEnumerator<'T> with - member x.Current = e.Current - interface IEnumerator with - member x.Current = (e :> IEnumerator).Current - member x.MoveNext() = e.MoveNext() - member x.Reset() = noReset() - interface System.IDisposable with - member x.Dispose() = - try - e.Dispose() - finally - f() - } - // Use generators for some implementations of IEnumerables. // module Generator = @@ -526,293 +426,6 @@ namespace Microsoft.FSharp.Collections | :? EnumeratorWrappingLazyGenerator<'T> as e -> e.Generator | _ -> (new LazyGeneratorWrappingEnumerator<'T>(e) :> Generator<'T>) -namespace Microsoft.FSharp.Core.CompilerServices - - open System - open System.Diagnostics - open Microsoft.FSharp.Core - open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators - open Microsoft.FSharp.Core.Operators - open Microsoft.FSharp.Control - open Microsoft.FSharp.Collections - open Microsoft.FSharp.Primitives.Basics - open System.Collections - open System.Collections.Generic - - module RuntimeHelpers = - - [] - type internal StructBox<'T when 'T:equality>(value:'T) = - member x.Value = value - static member Comparer = - let gcomparer = HashIdentity.Structural<'T> - { new IEqualityComparer> with - member __.GetHashCode(v) = gcomparer.GetHashCode(v.Value) - member __.Equals(v1,v2) = gcomparer.Equals(v1.Value,v2.Value) } - - let inline checkNonNull argName arg = - match box arg with - | null -> nullArg argName - | _ -> () - - let mkSeq f = - { new IEnumerable<'U> with - member x.GetEnumerator() = f() - interface IEnumerable with - member x.GetEnumerator() = (f() :> IEnumerator) } - - [] - type EmptyEnumerable<'T> = - | EmptyEnumerable - interface IEnumerable<'T> with - member x.GetEnumerator() = IEnumerator.Empty<'T>() - interface IEnumerable with - member x.GetEnumerator() = (IEnumerator.Empty<'T>() :> IEnumerator) - - let Generate openf compute closef = - mkSeq (fun () -> IEnumerator.generateWhileSome openf compute closef) - - let GenerateUsing (openf : unit -> ('U :> System.IDisposable)) compute = - Generate openf compute (fun (s:'U) -> s.Dispose()) - - let EnumerateFromFunctions opener moveNext current = - Generate - opener - (fun x -> if moveNext x then Some(current x) else None) - (fun x -> match box(x) with :? System.IDisposable as id -> id.Dispose() | _ -> ()) - - // A family of enumerators that can have additional 'finally' actions added to the enumerator through - // the use of mutation. This is used to 'push' the disposal action for a 'use' into the next enumerator. - // For example, - // seq { use x = ... - // while ... } - // results in the 'while' loop giving an adjustable enumerator. This is then adjusted by adding the disposal action - // from the 'use' into the enumerator. This means that we avoid constructing a two-deep enumerator chain in this - // common case. - type IFinallyEnumerator = - abstract AppendFinallyAction : (unit -> unit) -> unit - - /// A concrete implementation of IEnumerable that adds the given compensation to the "Dispose" chain of any - /// enumerators returned by the enumerable. - [] - type FinallyEnumerable<'T>(compensation: unit -> unit, restf: unit -> seq<'T>) = - interface IEnumerable<'T> with - member x.GetEnumerator() = - try - let ie = restf().GetEnumerator() - match ie with - | :? IFinallyEnumerator as a -> - a.AppendFinallyAction(compensation) - ie - | _ -> - IEnumerator.EnumerateThenFinally compensation ie - with e -> - compensation() - reraise() - interface IEnumerable with - member x.GetEnumerator() = ((x :> IEnumerable<'T>).GetEnumerator() :> IEnumerator) - - /// An optimized object for concatenating a sequence of enumerables - [] - type ConcatEnumerator<'T,'U when 'U :> seq<'T>>(sources: seq<'U>) = - let mutable outerEnum = sources.GetEnumerator() - let mutable currInnerEnum = IEnumerator.Empty() - - let mutable started = false - let mutable finished = false - let mutable compensations = [] - - [] // false = unchecked - val mutable private currElement : 'T - - member x.Finish() = - finished <- true - try - match currInnerEnum with - | null -> () - | _ -> - try - currInnerEnum.Dispose() - finally - currInnerEnum <- null - finally - try - match outerEnum with - | null -> () - | _ -> - try - outerEnum.Dispose() - finally - outerEnum <- null - finally - let rec iter comps = - match comps with - | [] -> () - | h::t -> - try h() finally iter t - try - compensations |> List.rev |> iter - finally - compensations <- [] - - member x.GetCurrent() = - IEnumerator.check started - if finished then IEnumerator.alreadyFinished() else x.currElement - - interface IFinallyEnumerator with - member x.AppendFinallyAction(f) = - compensations <- f :: compensations - - interface IEnumerator<'T> with - member x.Current = x.GetCurrent() - - interface IEnumerator with - member x.Current = box (x.GetCurrent()) - - member x.MoveNext() = - if not started then (started <- true) - if finished then false - else - let rec takeInner () = - // check the inner list - if currInnerEnum.MoveNext() then - x.currElement <- currInnerEnum.Current - true - else - // check the outer list - let rec takeOuter() = - if outerEnum.MoveNext() then - let ie = outerEnum.Current - // Optimization to detect the statically-allocated empty IEnumerables - match box ie with - | :? EmptyEnumerable<'T> -> - // This one is empty, just skip, don't call GetEnumerator, try again - takeOuter() - | _ -> - // OK, this one may not be empty. - // Don't forget to dispose of the enumerator for the inner list now we're done with it - currInnerEnum.Dispose() - currInnerEnum <- ie.GetEnumerator() - takeInner () - else - // We're done - x.Finish() - false - takeOuter() - takeInner () - - member x.Reset() = IEnumerator.noReset() - - interface System.IDisposable with - member x.Dispose() = - if not finished then - x.Finish() - - let EnumerateUsing (resource : 'T :> System.IDisposable) (rest: 'T -> #seq<'U>) = - (FinallyEnumerable((fun () -> match box resource with null -> () | _ -> resource.Dispose()), - (fun () -> rest resource :> seq<_>)) :> seq<_>) - - let mkConcatSeq (sources: seq<'U :> seq<'T>>) = - mkSeq (fun () -> new ConcatEnumerator<_,_>(sources) :> IEnumerator<'T>) - - let EnumerateWhile (g : unit -> bool) (b: seq<'T>) : seq<'T> = - let started = ref false - let curr = ref None - let getCurr() = - IEnumerator.check !started - match !curr with None -> IEnumerator.alreadyFinished() | Some x -> x - let start() = if not !started then (started := true) - - let finish() = (curr := None) - mkConcatSeq - (mkSeq (fun () -> - { new IEnumerator<_> with - member x.Current = getCurr() - interface IEnumerator with - member x.Current = box (getCurr()) - member x.MoveNext() = - start() - let keepGoing = (try g() with e -> finish (); reraise ()) in - if keepGoing then - curr := Some(b); true - else - finish(); false - member x.Reset() = IEnumerator.noReset() - interface System.IDisposable with - member x.Dispose() = () })) - - let EnumerateThenFinally (rest : seq<'T>) (compensation : unit -> unit) = - (FinallyEnumerable(compensation, (fun () -> rest)) :> seq<_>) - - let CreateEvent (add : 'Delegate -> unit) (remove : 'Delegate -> unit) (create : (obj -> 'Args -> unit) -> 'Delegate ) :IEvent<'Delegate,'Args> = - // Note, we implement each interface explicitly: this works around a bug in the CLR - // implementation on CompactFramework 3.7, used on Windows Phone 7 - { new obj() with - member x.ToString() = "" - interface IEvent<'Delegate,'Args> - interface IDelegateEvent<'Delegate> with - member x.AddHandler(h) = add h - member x.RemoveHandler(h) = remove h - interface System.IObservable<'Args> with - member x.Subscribe(r:IObserver<'Args>) = - let h = create (fun _ args -> r.OnNext(args)) - add h - { new System.IDisposable with - member x.Dispose() = remove h } } - - - [] - type GeneratedSequenceBase<'T>() = - let mutable redirectTo : GeneratedSequenceBase<'T> = Unchecked.defaultof<_> - let mutable redirect : bool = false - - abstract GetFreshEnumerator : unit -> IEnumerator<'T> - abstract GenerateNext : next:byref> -> int // 0 = Stop, 1 = Yield, 2 = Goto - abstract Close: unit -> unit - abstract CheckClose: bool - abstract LastGenerated : 'T - - //[] - member x.MoveNextImpl() = - let active = - if redirect then redirectTo - else x - let mutable target = null - match active.GenerateNext(&target) with - | 1 -> - true - | 2 -> - match target.GetEnumerator() with - | :? GeneratedSequenceBase<'T> as g when not active.CheckClose -> - redirectTo <- g - | e -> - redirectTo <- - { new GeneratedSequenceBase<'T>() with - member x.GetFreshEnumerator() = e - member x.GenerateNext(_) = if e.MoveNext() then 1 else 0 - member x.Close() = try e.Dispose() finally active.Close() - member x.CheckClose = true - member x.LastGenerated = e.Current } - redirect <- true - x.MoveNextImpl() - | _ (* 0 *) -> - false - - interface IEnumerable<'T> with - member x.GetEnumerator() = x.GetFreshEnumerator() - interface IEnumerable with - member x.GetEnumerator() = (x.GetFreshEnumerator() :> IEnumerator) - interface IEnumerator<'T> with - member x.Current = if redirect then redirectTo.LastGenerated else x.LastGenerated - member x.Dispose() = if redirect then redirectTo.Close() else x.Close() - interface IEnumerator with - member x.Current = box (if redirect then redirectTo.LastGenerated else x.LastGenerated) - - //[] - member x.MoveNext() = x.MoveNextImpl() - - member x.Reset() = raise <| new System.NotSupportedException() - namespace Microsoft.FSharp.Collections @@ -849,7 +462,8 @@ namespace Microsoft.FSharp.Collections #else #endif - open Microsoft.FSharp.Core.CompilerServices.RuntimeHelpers + open Microsoft.FSharp.Collections.Internal + open Microsoft.FSharp.Collections.IEnumerator let mkDelayedSeq (f: unit -> IEnumerable<'T>) = mkSeq (fun () -> f().GetEnumerator()) let mkUnfoldSeq f x = mkSeq (fun () -> IEnumerator.unfold f x) @@ -1089,7 +703,7 @@ namespace Microsoft.FSharp.Collections [] let concat sources = checkNonNull "sources" sources - mkConcatSeq sources + RuntimeHelpers.mkConcatSeq sources [] let length (source : seq<'T>) = @@ -1454,7 +1068,7 @@ namespace Microsoft.FSharp.Collections let groupByValueType (keyf:'T->'Key) (seq:seq<'T>) = seq |> groupByImpl HashIdentity.Structural<'Key> keyf id // Wrap a StructBox around all keys in case the key type is itself a type using null as a representation - let groupByRefType (keyf:'T->'Key) (seq:seq<'T>) = seq |> groupByImpl StructBox<'Key>.Comparer (fun t -> StructBox (keyf t)) (fun sb -> sb.Value) + let groupByRefType (keyf:'T->'Key) (seq:seq<'T>) = seq |> groupByImpl RuntimeHelpers.StructBox<'Key>.Comparer (fun t -> RuntimeHelpers.StructBox (keyf t)) (fun sb -> sb.Value) [] let groupBy (keyf:'T->'Key) (seq:seq<'T>) = @@ -1537,7 +1151,7 @@ namespace Microsoft.FSharp.Collections let countByValueType (keyf:'T->'Key) (seq:seq<'T>) = seq |> countByImpl HashIdentity.Structural<'Key> keyf id // Wrap a StructBox around all keys in case the key type is itself a type using null as a representation - let countByRefType (keyf:'T->'Key) (seq:seq<'T>) = seq |> countByImpl StructBox<'Key>.Comparer (fun t -> StructBox (keyf t)) (fun sb -> sb.Value) + let countByRefType (keyf:'T->'Key) (seq:seq<'T>) = seq |> countByImpl RuntimeHelpers.StructBox<'Key>.Comparer (fun t -> RuntimeHelpers.StructBox (keyf t)) (fun sb -> sb.Value) [] let countBy (keyf:'T->'Key) (source:seq<'T>) = diff --git a/src/fsharp/FSharp.Core/seq.fsi b/src/fsharp/FSharp.Core/seq.fsi index f05e9db76dd..7e081935b57 100644 --- a/src/fsharp/FSharp.Core/seq.fsi +++ b/src/fsharp/FSharp.Core/seq.fsi @@ -1342,97 +1342,3 @@ namespace Microsoft.FSharp.Collections /// Thrown when any of the input sequences is null. [] val zip3: source1:seq<'T1> -> source2:seq<'T2> -> source3:seq<'T3> -> seq<'T1 * 'T2 * 'T3> - -namespace Microsoft.FSharp.Core.CompilerServices - - open System - open System.Collections - open System.Collections.Generic - open Microsoft.FSharp.Core - open Microsoft.FSharp.Collections - - - [] - /// A group of functions used as part of the compiled representation of F# sequence expressions. - module RuntimeHelpers = - - [] - type internal StructBox<'T when 'T : equality> = - new : value:'T -> StructBox<'T> - member Value : 'T - static member Comparer : IEqualityComparer> - - /// The F# compiler emits calls to this function to - /// implement the while operator for F# sequence expressions. - /// - /// A function that indicates whether iteration should continue. - /// The input sequence. - /// - /// The result sequence. - val EnumerateWhile : guard:(unit -> bool) -> source:seq<'T> -> seq<'T> - - /// The F# compiler emits calls to this function to - /// implement the try/finally operator for F# sequence expressions. - /// - /// The input sequence. - /// A computation to be included in an enumerator's Dispose method. - /// - /// The result sequence. - val EnumerateThenFinally : source:seq<'T> -> compensation:(unit -> unit) -> seq<'T> - - /// The F# compiler emits calls to this function to implement the compiler-intrinsic - /// conversions from untyped System.Collections.IEnumerable sequences to typed sequences. - /// - /// An initializer function. - /// A function to iterate and test if end of sequence is reached. - /// A function to retrieve the current element. - /// - /// The resulting typed sequence. - val EnumerateFromFunctions: create:(unit -> 'T) -> moveNext:('T -> bool) -> current:('T -> 'U) -> seq<'U> - - /// The F# compiler emits calls to this function to implement the use operator for F# sequence - /// expressions. - /// - /// The resource to be used and disposed. - /// The input sequence. - /// - /// The result sequence. - val EnumerateUsing : resource:'T -> source:('T -> 'Collection) -> seq<'U> when 'T :> IDisposable and 'Collection :> seq<'U> - - /// Creates an anonymous event with the given handlers. - /// - /// A function to handle adding a delegate for the event to trigger. - /// A function to handle removing a delegate that the event triggers. - /// A function to produce the delegate type the event can trigger. - /// - /// The initialized event. - val CreateEvent : addHandler : ('Delegate -> unit) -> removeHandler : ('Delegate -> unit) -> createHandler : ((obj -> 'Args -> unit) -> 'Delegate) -> Microsoft.FSharp.Control.IEvent<'Delegate,'Args> - - [] - /// The F# compiler emits implementations of this type for compiled sequence expressions. - type GeneratedSequenceBase<'T> = - /// The F# compiler emits implementations of this type for compiled sequence expressions. - /// - /// A new sequence generator for the expression. - new : unit -> GeneratedSequenceBase<'T> - /// The F# compiler emits implementations of this type for compiled sequence expressions. - /// - /// A new enumerator for the sequence. - abstract GetFreshEnumerator : unit -> IEnumerator<'T> - /// The F# compiler emits implementations of this type for compiled sequence expressions. - /// - /// A reference to the sequence. - /// - /// A 0, 1, and 2 respectively indicate Stop, Yield, and Goto conditions for the sequence generator. - abstract GenerateNext : result:byref> -> int - /// The F# compiler emits implementations of this type for compiled sequence expressions. - abstract Close: unit -> unit - /// The F# compiler emits implementations of this type for compiled sequence expressions. - abstract CheckClose: bool - /// The F# compiler emits implementations of this type for compiled sequence expressions. - abstract LastGenerated : 'T - interface IEnumerable<'T> - interface IEnumerable - interface IEnumerator<'T> - interface IEnumerator - diff --git a/src/fsharp/FSharp.Core/seqcore.fs b/src/fsharp/FSharp.Core/seqcore.fs new file mode 100644 index 00000000000..e733d7f9a2d --- /dev/null +++ b/src/fsharp/FSharp.Core/seqcore.fs @@ -0,0 +1,405 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.FSharp.Collections + #nowarn "52" // The value has been copied to ensure the original is not mutated by this operation + + open System + open System.Diagnostics + open System.Collections + open System.Collections.Generic + open Microsoft.FSharp.Core + open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + open Microsoft.FSharp.Core.Operators + open Microsoft.FSharp.Control + open Microsoft.FSharp.Collections + + module internal IEnumerator = + + let noReset() = raise (new System.NotSupportedException(SR.GetString(SR.resetNotSupported))) + let notStarted() = raise (new System.InvalidOperationException(SR.GetString(SR.enumerationNotStarted))) + let alreadyFinished() = raise (new System.InvalidOperationException(SR.GetString(SR.enumerationAlreadyFinished))) + let check started = if not started then notStarted() + let dispose (r : System.IDisposable) = r.Dispose() + + let cast (e : IEnumerator) : IEnumerator<'T> = + { new IEnumerator<'T> with + member x.Current = unbox<'T> e.Current + interface IEnumerator with + member x.Current = unbox<'T> e.Current :> obj + member x.MoveNext() = e.MoveNext() + member x.Reset() = noReset() + interface System.IDisposable with + member x.Dispose() = + match e with + | :? System.IDisposable as e -> e.Dispose() + | _ -> () } + + /// A concrete implementation of an enumerator that returns no values + [] + type EmptyEnumerator<'T>() = + let mutable started = false + interface IEnumerator<'T> with + member x.Current = + check started + (alreadyFinished() : 'T) + + interface System.Collections.IEnumerator with + member x.Current = + check started + (alreadyFinished() : obj) + member x.MoveNext() = + if not started then started <- true + false + member x.Reset() = noReset() + interface System.IDisposable with + member x.Dispose() = () + + let Empty<'T> () = (new EmptyEnumerator<'T>() :> IEnumerator<'T>) + + [] + type EmptyEnumerable<'T> = + | EmptyEnumerable + interface IEnumerable<'T> with + member x.GetEnumerator() = Empty<'T>() + interface IEnumerable with + member x.GetEnumerator() = (Empty<'T>() :> IEnumerator) + + let readAndClear r = + lock r (fun () -> match !r with None -> None | Some _ as res -> r := None; res) + + let generateWhileSome openf compute closef : IEnumerator<'U> = + let started = ref false + let curr = ref None + let state = ref (Some(openf())) + let getCurr() = + check !started + match !curr with None -> alreadyFinished() | Some x -> x + let start() = if not !started then (started := true) + + let dispose() = readAndClear state |> Option.iter closef + let finish() = (try dispose() finally curr := None) + { new IEnumerator<'U> with + member x.Current = getCurr() + interface IEnumerator with + member x.Current = box (getCurr()) + member x.MoveNext() = + start() + match !state with + | None -> false (* we started, then reached the end, then got another MoveNext *) + | Some s -> + match (try compute s with e -> finish(); reraise()) with + | None -> finish(); false + | Some _ as x -> curr := x; true + + member x.Reset() = noReset() + interface System.IDisposable with + member x.Dispose() = dispose() } + + [] + type Singleton<'T>(v:'T) = + let mutable started = false + interface IEnumerator<'T> with + member x.Current = v + interface IEnumerator with + member x.Current = box v + member x.MoveNext() = if started then false else (started <- true; true) + member x.Reset() = noReset() + interface System.IDisposable with + member x.Dispose() = () + + let Singleton x = (new Singleton<'T>(x) :> IEnumerator<'T>) + + let EnumerateThenFinally f (e : IEnumerator<'T>) = + { new IEnumerator<'T> with + member x.Current = e.Current + interface IEnumerator with + member x.Current = (e :> IEnumerator).Current + member x.MoveNext() = e.MoveNext() + member x.Reset() = noReset() + interface System.IDisposable with + member x.Dispose() = + try + e.Dispose() + finally + f() + } + + let inline checkNonNull argName arg = + match box arg with + | null -> nullArg argName + | _ -> () + + let mkSeq f = + { new IEnumerable<'U> with + member x.GetEnumerator() = f() + interface IEnumerable with + member x.GetEnumerator() = (f() :> IEnumerator) } + +namespace Microsoft.FSharp.Core.CompilerServices + + open System + open System.Diagnostics + open Microsoft.FSharp.Core + open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + open Microsoft.FSharp.Core.Operators + open Microsoft.FSharp.Control + open Microsoft.FSharp.Collections + open Microsoft.FSharp.Collections.IEnumerator + open Microsoft.FSharp.Primitives.Basics + open System.Collections + open System.Collections.Generic + + module RuntimeHelpers = + + [] + type internal StructBox<'T when 'T:equality>(value:'T) = + member x.Value = value + static member Comparer = + let gcomparer = HashIdentity.Structural<'T> + { new IEqualityComparer> with + member __.GetHashCode(v) = gcomparer.GetHashCode(v.Value) + member __.Equals(v1,v2) = gcomparer.Equals(v1.Value,v2.Value) } + + let Generate openf compute closef = + mkSeq (fun () -> IEnumerator.generateWhileSome openf compute closef) + + let GenerateUsing (openf : unit -> ('U :> System.IDisposable)) compute = + Generate openf compute (fun (s:'U) -> s.Dispose()) + + let EnumerateFromFunctions opener moveNext current = + Generate + opener + (fun x -> if moveNext x then Some(current x) else None) + (fun x -> match box(x) with :? System.IDisposable as id -> id.Dispose() | _ -> ()) + + // A family of enumerators that can have additional 'finally' actions added to the enumerator through + // the use of mutation. This is used to 'push' the disposal action for a 'use' into the next enumerator. + // For example, + // seq { use x = ... + // while ... } + // results in the 'while' loop giving an adjustable enumerator. This is then adjusted by adding the disposal action + // from the 'use' into the enumerator. This means that we avoid constructing a two-deep enumerator chain in this + // common case. + type IFinallyEnumerator = + abstract AppendFinallyAction : (unit -> unit) -> unit + + /// A concrete implementation of IEnumerable that adds the given compensation to the "Dispose" chain of any + /// enumerators returned by the enumerable. + [] + type FinallyEnumerable<'T>(compensation: unit -> unit, restf: unit -> seq<'T>) = + interface IEnumerable<'T> with + member x.GetEnumerator() = + try + let ie = restf().GetEnumerator() + match ie with + | :? IFinallyEnumerator as a -> + a.AppendFinallyAction(compensation) + ie + | _ -> + IEnumerator.EnumerateThenFinally compensation ie + with e -> + compensation() + reraise() + interface IEnumerable with + member x.GetEnumerator() = ((x :> IEnumerable<'T>).GetEnumerator() :> IEnumerator) + + /// An optimized object for concatenating a sequence of enumerables + [] + type ConcatEnumerator<'T,'U when 'U :> seq<'T>>(sources: seq<'U>) = + let mutable outerEnum = sources.GetEnumerator() + let mutable currInnerEnum = IEnumerator.Empty() + + let mutable started = false + let mutable finished = false + let mutable compensations = [] + + [] // false = unchecked + val mutable private currElement : 'T + + member x.Finish() = + finished <- true + try + match currInnerEnum with + | null -> () + | _ -> + try + currInnerEnum.Dispose() + finally + currInnerEnum <- null + finally + try + match outerEnum with + | null -> () + | _ -> + try + outerEnum.Dispose() + finally + outerEnum <- null + finally + let rec iter comps = + match comps with + | [] -> () + | h::t -> + try h() finally iter t + try + compensations |> List.rev |> iter + finally + compensations <- [] + + member x.GetCurrent() = + IEnumerator.check started + if finished then IEnumerator.alreadyFinished() else x.currElement + + interface IFinallyEnumerator with + member x.AppendFinallyAction(f) = + compensations <- f :: compensations + + interface IEnumerator<'T> with + member x.Current = x.GetCurrent() + + interface IEnumerator with + member x.Current = box (x.GetCurrent()) + + member x.MoveNext() = + if not started then (started <- true) + if finished then false + else + let rec takeInner () = + // check the inner list + if currInnerEnum.MoveNext() then + x.currElement <- currInnerEnum.Current + true + else + // check the outer list + let rec takeOuter() = + if outerEnum.MoveNext() then + let ie = outerEnum.Current + // Optimization to detect the statically-allocated empty IEnumerables + match box ie with + | :? EmptyEnumerable<'T> -> + // This one is empty, just skip, don't call GetEnumerator, try again + takeOuter() + | _ -> + // OK, this one may not be empty. + // Don't forget to dispose of the enumerator for the inner list now we're done with it + currInnerEnum.Dispose() + currInnerEnum <- ie.GetEnumerator() + takeInner () + else + // We're done + x.Finish() + false + takeOuter() + takeInner () + + member x.Reset() = IEnumerator.noReset() + + interface System.IDisposable with + member x.Dispose() = + if not finished then + x.Finish() + + let EnumerateUsing (resource : 'T :> System.IDisposable) (rest: 'T -> #seq<'U>) = + (FinallyEnumerable((fun () -> match box resource with null -> () | _ -> resource.Dispose()), + (fun () -> rest resource :> seq<_>)) :> seq<_>) + + let mkConcatSeq (sources: seq<'U :> seq<'T>>) = + mkSeq (fun () -> new ConcatEnumerator<_,_>(sources) :> IEnumerator<'T>) + + let EnumerateWhile (g : unit -> bool) (b: seq<'T>) : seq<'T> = + let started = ref false + let curr = ref None + let getCurr() = + IEnumerator.check !started + match !curr with None -> IEnumerator.alreadyFinished() | Some x -> x + let start() = if not !started then (started := true) + + let finish() = (curr := None) + mkConcatSeq + (mkSeq (fun () -> + { new IEnumerator<_> with + member x.Current = getCurr() + interface IEnumerator with + member x.Current = box (getCurr()) + member x.MoveNext() = + start() + let keepGoing = (try g() with e -> finish (); reraise ()) in + if keepGoing then + curr := Some(b); true + else + finish(); false + member x.Reset() = IEnumerator.noReset() + interface System.IDisposable with + member x.Dispose() = () })) + + let EnumerateThenFinally (rest : seq<'T>) (compensation : unit -> unit) = + (FinallyEnumerable(compensation, (fun () -> rest)) :> seq<_>) + + let CreateEvent (add : 'Delegate -> unit) (remove : 'Delegate -> unit) (create : (obj -> 'Args -> unit) -> 'Delegate ) :IEvent<'Delegate,'Args> = + // Note, we implement each interface explicitly: this works around a bug in the CLR + // implementation on CompactFramework 3.7, used on Windows Phone 7 + { new obj() with + member x.ToString() = "" + interface IEvent<'Delegate,'Args> + interface IDelegateEvent<'Delegate> with + member x.AddHandler(h) = add h + member x.RemoveHandler(h) = remove h + interface System.IObservable<'Args> with + member x.Subscribe(r:IObserver<'Args>) = + let h = create (fun _ args -> r.OnNext(args)) + add h + { new System.IDisposable with + member x.Dispose() = remove h } } + + + [] + type GeneratedSequenceBase<'T>() = + let mutable redirectTo : GeneratedSequenceBase<'T> = Unchecked.defaultof<_> + let mutable redirect : bool = false + + abstract GetFreshEnumerator : unit -> IEnumerator<'T> + abstract GenerateNext : next:byref> -> int // 0 = Stop, 1 = Yield, 2 = Goto + abstract Close: unit -> unit + abstract CheckClose: bool + abstract LastGenerated : 'T + + //[] + member x.MoveNextImpl() = + let active = + if redirect then redirectTo + else x + let mutable target = null + match active.GenerateNext(&target) with + | 1 -> + true + | 2 -> + match target.GetEnumerator() with + | :? GeneratedSequenceBase<'T> as g when not active.CheckClose -> + redirectTo <- g + | e -> + redirectTo <- + { new GeneratedSequenceBase<'T>() with + member x.GetFreshEnumerator() = e + member x.GenerateNext(_) = if e.MoveNext() then 1 else 0 + member x.Close() = try e.Dispose() finally active.Close() + member x.CheckClose = true + member x.LastGenerated = e.Current } + redirect <- true + x.MoveNextImpl() + | _ (* 0 *) -> + false + + interface IEnumerable<'T> with + member x.GetEnumerator() = x.GetFreshEnumerator() + interface IEnumerable with + member x.GetEnumerator() = (x.GetFreshEnumerator() :> IEnumerator) + interface IEnumerator<'T> with + member x.Current = if redirect then redirectTo.LastGenerated else x.LastGenerated + member x.Dispose() = if redirect then redirectTo.Close() else x.Close() + interface IEnumerator with + member x.Current = box (if redirect then redirectTo.LastGenerated else x.LastGenerated) + + //[] + member x.MoveNext() = x.MoveNextImpl() + + member x.Reset() = raise <| new System.NotSupportedException() diff --git a/src/fsharp/FSharp.Core/seqcore.fsi b/src/fsharp/FSharp.Core/seqcore.fsi new file mode 100644 index 00000000000..ebd7accb8e5 --- /dev/null +++ b/src/fsharp/FSharp.Core/seqcore.fsi @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.FSharp.Collections + open System + open System.Collections + open System.Collections.Generic + open Microsoft.FSharp.Core + open Microsoft.FSharp.Collections + module internal IEnumerator = + val noReset : unit -> 'a + val notStarted : unit -> 'a + val alreadyFinished : unit -> 'a + val check : started:bool -> unit + val dispose : r:System.IDisposable -> unit + val cast : + e:System.Collections.IEnumerator -> + System.Collections.Generic.IEnumerator<'T> + [] + type EmptyEnumerator<'T> = + class + interface System.IDisposable + interface System.Collections.IEnumerator + interface System.Collections.Generic.IEnumerator<'T> + new : unit -> EmptyEnumerator<'T> + end + val Empty : unit -> System.Collections.Generic.IEnumerator<'T> + [] + type EmptyEnumerable<'T> = + | EmptyEnumerable + with + interface System.Collections.IEnumerable + interface System.Collections.Generic.IEnumerable<'T> + end + + val readAndClear : r:'a option ref -> 'a option + val generateWhileSome : + openf:(unit -> 'a) -> + compute:('a -> 'U option) -> + closef:('a -> unit) -> System.Collections.Generic.IEnumerator<'U> + [] + type Singleton<'T> = + class + interface System.IDisposable + interface System.Collections.IEnumerator + interface System.Collections.Generic.IEnumerator<'T> + new : v:'T -> Singleton<'T> + end + val Singleton : x:'T -> System.Collections.Generic.IEnumerator<'T> + val EnumerateThenFinally : + f:(unit -> unit) -> + e:System.Collections.Generic.IEnumerator<'T> -> + System.Collections.Generic.IEnumerator<'T> + val inline checkNonNull : argName:string -> arg:'a -> unit + val mkSeq : + f:(unit -> System.Collections.Generic.IEnumerator<'U>) -> + System.Collections.Generic.IEnumerable<'U> + +namespace Microsoft.FSharp.Core.CompilerServices + + open System + open System.Collections + open System.Collections.Generic + open Microsoft.FSharp.Core + open Microsoft.FSharp.Collections + + [] + /// A group of functions used as part of the compiled representation of F# sequence expressions. + module RuntimeHelpers = + + [] + type internal StructBox<'T when 'T : equality> = + new : value:'T -> StructBox<'T> + member Value : 'T + static member Comparer : IEqualityComparer> + + val internal mkConcatSeq : sources:(seq<#seq<'T>>) -> seq<'T> + + /// The F# compiler emits calls to this function to + /// implement the while operator for F# sequence expressions. + /// + /// A function that indicates whether iteration should continue. + /// The input sequence. + /// + /// The result sequence. + val EnumerateWhile : guard:(unit -> bool) -> source:seq<'T> -> seq<'T> + + /// The F# compiler emits calls to this function to + /// implement the try/finally operator for F# sequence expressions. + /// + /// The input sequence. + /// A computation to be included in an enumerator's Dispose method. + /// + /// The result sequence. + val EnumerateThenFinally : source:seq<'T> -> compensation:(unit -> unit) -> seq<'T> + + /// The F# compiler emits calls to this function to implement the compiler-intrinsic + /// conversions from untyped System.Collections.IEnumerable sequences to typed sequences. + /// + /// An initializer function. + /// A function to iterate and test if end of sequence is reached. + /// A function to retrieve the current element. + /// + /// The resulting typed sequence. + val EnumerateFromFunctions: create:(unit -> 'T) -> moveNext:('T -> bool) -> current:('T -> 'U) -> seq<'U> + + /// The F# compiler emits calls to this function to implement the use operator for F# sequence + /// expressions. + /// + /// The resource to be used and disposed. + /// The input sequence. + /// + /// The result sequence. + val EnumerateUsing : resource:'T -> source:('T -> 'Collection) -> seq<'U> when 'T :> IDisposable and 'Collection :> seq<'U> + + /// Creates an anonymous event with the given handlers. + /// + /// A function to handle adding a delegate for the event to trigger. + /// A function to handle removing a delegate that the event triggers. + /// A function to produce the delegate type the event can trigger. + /// + /// The initialized event. + val CreateEvent : addHandler : ('Delegate -> unit) -> removeHandler : ('Delegate -> unit) -> createHandler : ((obj -> 'Args -> unit) -> 'Delegate) -> Microsoft.FSharp.Control.IEvent<'Delegate,'Args> + + [] + /// The F# compiler emits implementations of this type for compiled sequence expressions. + type GeneratedSequenceBase<'T> = + /// The F# compiler emits implementations of this type for compiled sequence expressions. + /// + /// A new sequence generator for the expression. + new : unit -> GeneratedSequenceBase<'T> + /// The F# compiler emits implementations of this type for compiled sequence expressions. + /// + /// A new enumerator for the sequence. + abstract GetFreshEnumerator : unit -> IEnumerator<'T> + /// The F# compiler emits implementations of this type for compiled sequence expressions. + /// + /// A reference to the sequence. + /// + /// A 0, 1, and 2 respectively indicate Stop, Yield, and Goto conditions for the sequence generator. + abstract GenerateNext : result:byref> -> int + /// The F# compiler emits implementations of this type for compiled sequence expressions. + abstract Close: unit -> unit + /// The F# compiler emits implementations of this type for compiled sequence expressions. + abstract CheckClose: bool + /// The F# compiler emits implementations of this type for compiled sequence expressions. + abstract LastGenerated : 'T + interface IEnumerable<'T> + interface IEnumerable + interface IEnumerator<'T> + interface IEnumerator