| | | 1 | | namespace FSharp.Interop.Dlr |
| | | 2 | | |
| | | 3 | | // 3513: "resumable code invocation" at `code.Invoke(&sm)` in Run, expected when defining a |
| | | 4 | | // resumable builder (FSharp.Core's task builder suppresses it the same way). |
| | | 5 | | #nowarn "3513" |
| | | 6 | | |
| | | 7 | | open System |
| | | 8 | | open System.Diagnostics.CodeAnalysis |
| | | 9 | | open System.Runtime.CompilerServices |
| | | 10 | | open System.Runtime.InteropServices |
| | | 11 | | open Microsoft.FSharp.Core.CompilerServices |
| | | 12 | | open Microsoft.FSharp.Core.CompilerServices.StateMachineHelpers |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Builder for <c>dlr { }</c>. The body is never executed as written: it is compiled as F# |
| | | 16 | | /// resumable code into a struct state machine whose fields are the block's captured variables, |
| | | 17 | | /// the block's quotation comes from the <c>[<ReflectedDefinition>]</c> on the function or |
| | | 18 | | /// member containing it (or on an enclosing type or module), and it is compiled once into a |
| | | 19 | | /// delegate whose DLR call sites are constants and whose input is that struct. Where the |
| | | 20 | | /// compiler cannot build the state machine (Debug builds), the block's Delay closure plays |
| | | 21 | | /// the same role. |
| | | 22 | | /// </summary> |
| | | 23 | | [<Sealed>] |
| | 4 | 24 | | type DlrBuilder() = |
| | | 25 | | /// <summary>Returns the body unevaluated, wrapped as resumable code; in the fallback path <c>Run</c> reads the clos |
| | | 26 | | member inline _.Delay([<InlineIfLambda>] delayed: unit -> ResumableCode<'D, 'T>) : ResumableCode<'D, 'T> = |
| | 808785 | 27 | | ResumableCode<'D, 'T>(fun sm -> (delayed ()).Invoke(&sm)) |
| | | 28 | | |
| | | 29 | | // Return through Using are inlined at every call site in Debug and Release alike, so their |
| | | 30 | | // compiled methods are never called and are excluded from coverage. (Delay and Run are |
| | | 31 | | // called in Debug, so they stay.) |
| | | 32 | | [<ExcludeFromCodeCoverage>] |
| | | 33 | | member inline _.Return(value: 'T) : ResumableCode<DlrData<'T>, 'T> = |
| | | 34 | | ResumableCode<DlrData<'T>, 'T>(fun sm -> sm.Data.Result <- value; true) |
| | | 35 | | |
| | | 36 | | [<ExcludeFromCodeCoverage>] |
| | | 37 | | member inline _.Zero() : ResumableCode<'D, unit> = ResumableCode.Zero() |
| | | 38 | | |
| | | 39 | | [<ExcludeFromCodeCoverage>] |
| | | 40 | | member inline _.Combine(first: ResumableCode<'D, unit>, rest: ResumableCode<'D, 'T>) : ResumableCode<'D, 'T> = |
| | | 41 | | ResumableCode.Combine(first, rest) |
| | | 42 | | |
| | | 43 | | [<ExcludeFromCodeCoverage>] |
| | | 44 | | member inline _.For(items: seq<'X>, [<InlineIfLambda>] body: 'X -> ResumableCode<'D, unit>) : ResumableCode<'D, unit |
| | | 45 | | ResumableCode.For(items, body) |
| | | 46 | | |
| | | 47 | | [<ExcludeFromCodeCoverage>] |
| | | 48 | | member inline _.While([<InlineIfLambda>] guard: unit -> bool, body: ResumableCode<'D, unit>) : ResumableCode<'D, uni |
| | | 49 | | ResumableCode.While(guard, body) |
| | | 50 | | |
| | | 51 | | [<ExcludeFromCodeCoverage>] |
| | | 52 | | member inline _.TryWith(body: ResumableCode<'D, 'T>, [<InlineIfLambda>] handler: exn -> ResumableCode<'D, 'T>) : Res |
| | | 53 | | ResumableCode.TryWith(body, handler) |
| | | 54 | | |
| | | 55 | | [<ExcludeFromCodeCoverage>] |
| | | 56 | | member inline _.TryFinally(body: ResumableCode<'D, 'T>, [<InlineIfLambda>] compensation: unit -> unit) : ResumableCo |
| | | 57 | | ResumableCode.TryFinally(body, ResumableCode<'D, unit>(fun _ -> compensation (); true)) |
| | | 58 | | |
| | | 59 | | [<ExcludeFromCodeCoverage>] |
| | | 60 | | member inline _.Using(resource: 'R, [<InlineIfLambda>] body: 'R -> ResumableCode<'D, 'T>) : ResumableCode<'D, 'T> wh |
| | | 61 | | ResumableCode.Using(resource, body) |
| | | 62 | | |
| | | 63 | | member inline this.Run(code: ResumableCode<DlrData<'T>, 'T>, |
| | | 64 | | [<CallerFilePath; Optional; DefaultParameterValue("")>] file: string, |
| | | 65 | | [<CallerLineNumber; Optional; DefaultParameterValue(0)>] line: int) : 'T = |
| | | 66 | | if __useResumableCode then |
| | | 67 | | // The compiler builds a struct per block: the captured variables are its fields, its |
| | | 68 | | // type is a JIT constant in Machine, and MoveNext (the body as written) is never called. |
| | | 69 | | __stateMachine<DlrData<'T>, 'T> |
| | | 70 | | (MoveNextMethodImpl<DlrData<'T>>(fun sm -> __resumeAt sm.ResumptionPoint; code.Invoke(&sm) |> ignore)) |
| | | 71 | | (SetStateMachineMethodImpl<DlrData<'T>>(fun _ _ -> ())) |
| | | 72 | | (AfterCode<DlrData<'T>, 'T>(fun sm -> DlrRun.Machine(this, &sm, file, line))) |
| | | 73 | | else |
| | 808785 | 74 | | DlrRun.Closure(this, code, file, line) |
| | | 75 | | |
| | | 76 | | [<AutoOpen>] |
| | | 77 | | module Builder = |
| | | 78 | | let dlr = DlrBuilder() |