< Summary - FSharp.Interop.Dlr

Information
Class: FSharp.Interop.Dlr.Translate
Assembly: FSharp.Interop.Dlr
File(s): /home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/Translate.fs
Line coverage
93%
Covered lines: 128
Uncovered lines: 9
Coverable lines: 137
Total lines: 242
Line coverage: 93.4%
Branch coverage
85%
Covered branches: 173
Total branches: 202
Branch coverage: 85.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%210%
translate(...)61.11%181893.02%
isCaptured@65(...)100%22100%
Invoke(...)100%22100%
isBuilder@67(...)100%22100%
Invoke(...)100%22100%
rewriteIn@75(...)94.68%949498.27%
Invoke(...)94.31%888896.82%
tupleVar@90(...)61.11%623672.72%
Invoke(...)55.88%1433454.54%
subst@126(...)100%1010100%
Invoke(...)100%1010100%
readCell@141(...)100%11100%
Invoke(...)100%11100%
viaCells@142(...)100%11100%
Invoke(...)100%11100%
Invoke(...)100%11100%
unitBranches@177(...)100%66100%
Invoke(...)100%66100%
peel@188(...)50%5466.66%
Invoke(...)50%5466.66%

File(s)

/home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/Translate.fs

#LineLine coverage
 1namespace FSharp.Interop.Dlr
 2
 3open System
 4open System.Linq.Expressions
 5open FSharp.Quotations
 6open FSharp.Quotations.Patterns
 7open FSharp.Quotations.DerivedPatterns
 8open FSharp.Quotations.ExprShape
 9open FSharp.Reflection
 10open Microsoft.FSharp.Linq.RuntimeHelpers
 11open System.Runtime.CompilerServices
 12
 13/// Turns the reflected body of a `dlr { }` block into a delegate over the block's compiler-generated
 14/// container — a `DlrReader<'SM, 'T>` over its state machine struct, or a `Func<obj, 'T>` over its
 15/// Delay closure — with the DLR call sites baked in as constants.
 16module internal Translate =
 17    open TranslatePatterns
 18    open SiteHoisting
 19    open TranslateBlock
 20    open TranslateMembers
 21
 22    /// A compiled `dlr { }` site: a `DlrReader<'SM, 'T>` over the state machine struct, or in the
 23    /// fallback path a `Func<obj, 'T>` over the Delay closure object.
 24    type Compiled = { Delegate: Delegate }
 25
 26    /// Receives each block's expression tree, with the block's container type, once its sites are
 27    /// hoisted: on the state-machine path that is before the by-reference wrapper, which only
 28    /// copies the machine into the lambda's `sm` and is what `Compile()` then gets. Null, and then
 29    /// free, unless set: `docs/trees` sets it by reflection to generate the docs' examples.
 030    type TreeHook private () =
 31        static member val Sink : Action<Type, LambdaExpression> = null with get, set
 32
 33    /// Compiles the reflected body of one `dlr { }` block. `closureType` is the block's
 34    /// compiler-generated container — its state machine struct, or in the fallback path the
 35    /// class of its `Delay` closure: its fields, named after the captured variables, are where
 36    /// the body's free variables are read from at call time.
 37    let translate (name: string) (builderType: Type) (context: Type) (memberBody: Expr) (closureType: Type) (resultType:
 1694838        let closure = if closureType.IsValueType then Var("sm", closureType) else Var("closure", typeof<obj>)
 847439        let fields = Captures.fields closureType
 847440        let reached, aliases = Captures.reached fields memberBody body
 847441        let block =
 847442            { BuilderType = builderType
 847443              Context = context
 847444              MemberBody = memberBody
 847445              ClosureType = closureType
 847446              Closure = closure
 847447              Fields = fields
 847448              Substituted = aliases
 847449              Ambiguous =
 847450                  reached
 4497651                  |> List.countBy (fun v -> v.Name) |> List.filter (fun (_, n) -> n > 1) |> List.map fst |> Set.ofList
 847452                  // Only an optimized build splits a tuple (the state machine, or in Release the closure
 847453                  // of `(dlr { … } : unit -> R) ()`), and not one it captures whole as a field.
 847454                  |> Set.union (
 847455                      let optimized =
 847456                          closureType.IsValueType
 850657                          || not (closureType.Assembly.GetCustomAttributes(typeof<Diagnostics.DebuggableAttribute>, fals
 1278458                                  |> Seq.exists (fun a -> (a :?> Diagnostics.DebuggableAttribute).IsJITOptimizerDisabled
 2381159                      if optimized then Captures.elementNameClashes (fun t -> not (fields.ContainsKey t)) memberBody els
 847460              Name = name
 847461              Names = Collections.Generic.Dictionary() }
 62
 63        /// Variables bound inside the expression being rewritten are left alone; anything else
 64        /// that is not the builder comes from the closure or the enclosing member.
 2752765        let isCaptured (bound: Set<Var>) (v: Var) = not (bound.Contains v) && v.Type <> block.BuilderType
 66
 67        let isBuilder (receiver: Expr option) =
 4141568            match receiver with
 1573069            | Some r -> r.Type = block.BuilderType
 2568570            | None -> false
 71
 72        /// Builder calls and marker operations go to their sections; everything else is generic
 73        /// rewriting: captured variables become closure reads, and the structure is rebuilt as-is
 74        /// except where the expression tree has no form for it.
 75        let rec rewriteIn (bound: Set<Var>) (e: Expr) : Expr =
 15456176            let rewrite = rewriteIn bound
 15456177            match e with
 5502978            | Call(receiver, mi, args) when isBuilder receiver -> Plumbing.call block rewriteIn bound mi args e
 79            | _ ->
 14094780            match Members.tryOperation block rewriteIn bound e with
 1011481            | Some rewritten -> rewritten
 82            | None ->
 83            // A mutable struct captured from outside the block, mutated in place (see `inPlace`).
 13181984            match inPlace (fun v -> isCaptured bound v && Captures.isCell block v) (Captures.read block rewrite) (Captur
 2885            | Some written -> written
 86            | None ->
 87            // An element of a captured tuple the optimizer split into a field per element, read
 88            // directly or through what it inlines: a local function applied to unit, or an
 89            // immutable alias of an immutable variable (that value) whose name is no other's.
 90            let rec tupleVar (e: Expr) : Var option =
 176091                match e with
 184092                | Var v when isCaptured bound v && FSharp.Reflection.FSharpType.IsTuple v.Type ->
 8493                    match letDefinition v memberBody with
 094                    | Some(Var y as d) when block.Substituted.Contains v || (not v.IsMutable && not y.IsMutable && not (
 095                        tupleVar d |> Option.orElse (Some v)
 8496                    | _ -> Some v
 1297                | Application(Var f, Value(_, t)) when t = typeof<unit> && isCaptured bound f && not (block.Fields.Conta
 298                    match letDefinition f memberBody with
 299                    | Some(Lambda(_, b)) -> tupleVar b
 0100                    | _ -> None
 1674101                | _ -> None
 102            let element =
 130679103                match e with
 104                | TupleGet(inner, i) ->
 2524105                    tupleVar inner |> Option.bind (fun v -> Captures.element block v i)
 9236106                | Call(None, mi, [ inner ]) when mi.DeclaringType.FullName = "Microsoft.FSharp.Core.Operators" && (mi.Na
 346107                    tupleVar inner |> Option.bind (fun v -> Captures.element block v (if mi.Name = "Fst" then 0 else 1))
 128921108                | _ -> None
 130673109            match element with
 20110            | Some read -> read
 111            | None ->
 130653112            match e with
 46050113            | Var v when isCaptured bound v -> Captures.read block (rewriteIn bound) v
 264114            | VarSet(v, value) when isCaptured bound v -> Captures.assign block v (rewrite value)
 34630115            | ShapeVar _ -> e
 116            // A resumable-code-typed leftover of the builder's shape (see `codeType`): the
 117            // compiler's `null` after the rethrow in an unmatched `try … with` arm.
 2446118            | Value(null, t) when isCode t -> defaultOf (codeType t)
 119            // A `let mutable` of the block has no expression-tree form: loop and try bodies are
 120            // compiled into delegates, and a tree variable cannot be assigned from inside one. So
 121            // it lives in a reference cell, as the compiler does for a captured mutable, with
 122            // reads and writes going through the cell.
 13474123            | Let(v, def, letBody) when v.IsMutable ->
 1782124                let cell = Var(v.Name, typedefof<Ref<_>>.MakeGenericType v.Type)
 1782125                let value = cell.Type.GetProperty("Value")
 126                let rec subst (e: Expr) =
 49106127                    match inPlace ((=) v) (fun _ -> Expr.PropertyGet(Expr.Var cell, value)) (fun _ x -> Expr.PropertySet
 960128                    | Some written -> written
 129                    | None ->
 46226130                    match e with
 2516131                    | VarSet(v', x) when v' = v -> Expr.PropertySet(Expr.Var cell, value, subst x)
 17230132                    | Var v' when v' = v -> Expr.PropertyGet(Expr.Var cell, value)
 42508133                    | _ -> Quotation.rebuild subst e
 1782134                Expr.Let(cell, Expr.NewObject(cell.Type.GetConstructor([| v.Type |]), [ rewrite def ]), rewriteIn (bound
 11692135            | Let(v, def, letBody) -> Expr.Let(v, rewrite def, rewriteIn (bound.Add v) letBody)
 136            // `let rec` has no expression-tree form; tie the knot through reference cells, as the
 137            // compiler does: each binding becomes a cell, uses read the cell, and the definitions
 138            // are assigned after all cells exist so mutual recursion works too.
 139            | LetRecursive(bindings, letBody) ->
 620140                let cells = [ for (v, _) in bindings -> v, Var(v.Name + "'", typedefof<Ref<_>>.MakeGenericType v.Type) ]
 250141                let readCell (cell: Var) = Expr.PropertyGet(Expr.Var cell, cell.Type.GetProperty("Value"))
 3904142                let viaCells (e: Expr) = e.Substitute(fun v -> cells |> List.tryFind (fun (rv, _) -> rv = v) |> Option.m
 306143                let bound = cells |> List.fold (fun (b: Set<Var>) (_, c) -> b.Add c) bound
 120144                let assignments =
 370145                    [ for (_, def), (_, cell) in List.zip bindings cells ->
 250146                        Expr.PropertySet(Expr.Var cell, cell.Type.GetProperty("Value"), rewriteIn bound (viaCells def)) 
 120147                let body = rewriteIn bound (viaCells letBody)
 186148                let inner = List.foldBack (fun assign rest -> Expr.Sequential(assign, rest)) assignments body
 120149                List.foldBack
 120150                    (fun (v: Var, cell: Var) rest ->
 130151                        Expr.Let(cell, Expr.NewObject(cell.Type.GetConstructor [| v.Type |], [ Expr.Value(null, v.Type) 
 120152                    cells inner
 153            // A `try` that is not the builder's (under a lambda, inside a delegate literal, or a
 154            // `try … with` used as a value): LeafExpressionConverter has no form for it, so it runs
 155            // through the same delegates as the block's own (#158). F#'s filter only repeats the
 156            // handler's match, which ends in `reraise ()` where no case fits; the handler alone does.
 157            | TryWith(body, _, _, ex, handler) ->
 256158                let func = Plumbing.func block rewriteIn bound
 512159                Expr.Call(tryWith.MakeGenericMethod(e.Type), [ func [] body; func [ ex ] (Plumbing.rethrowing ex handler
 160            // `while` and `for i in a .. b` that are not the builder's (under a lambda, in a delegate
 161            // literal, inside an expression): no LINQ form either, so the same delegates (#162).
 162            | WhileLoop(guard, body) ->
 48163                let func = Plumbing.func block rewriteIn bound
 96164                Expr.Call(whileLoop, [ func [] guard; func [] body ])
 165            | ForIntegerRangeLoop(i, low, high, body) ->
 50166                let func = Plumbing.func block rewriteIn bound
 100167                Expr.Call(forRange, [ rewrite low; rewrite high; func [ i ] body ])
 168            // An element of a struct tuple: the converter's TupleGet looks for a reference tuple's
 169            // property and rejects a ValueTuple's index (#162), so it is the field itself
 170            // (`Item1`…`Item7`, then `Rest`), as the byref holder reads it.
 1680171            | TupleGet(tuple, index) when tuple.Type.IsValueType ->
 480172                Binders.byRefHolderPath tuple.Type index |> List.fold (fun e f -> Expr.FieldGet(e, f)) (rewrite tuple)
 173            | TryFinally(body, compensation) ->
 100174                let func = Plumbing.func block rewriteIn bound
 175                // `use`'s compensation is `if (d :? IDisposable) then d.Dispose() else ()`: a void
 176                // call and `()` as the branches, which the converter rejects as mismatched.
 177                let rec unitBranches (c: Expr) =
 400178                    match c with
 200179                    | IfThenElse(cond, a, b) when c.Type = typeof<unit> -> Expr.IfThenElse(cond, unitBranches a, unitBra
 400180                    | _ when isUnitCall c -> Expr.Sequential(c, Expr.Value(()))
 200181                    | _ -> c
 200182                Expr.Call(tryFinally.MakeGenericMethod(e.Type), [ func [] body; func [] (unitBranches compensation) ])
 183            | NewDelegate(t, vars, delegateBody) ->
 184                // A delegate literal's lambda is the delegate itself, not an F# function: keep it
 185                // whole (on wasm, made capturing: see `capturing`). The quotation may give the
 186                // parameters as nested lambdas in the body rather than in `vars`.
 1030187                let n = (DelegateMembers.invokeOf t).GetParameters().Length
 188                let rec peel k (e: Expr) acc =
 1030189                    match e with
 0190                    | Lambda(v, b) when k > 0 -> peel (k - 1) b (v :: acc)
 1030191                    | _ -> List.rev acc, e
 1030192                let peeled, body = peel (n - vars.Length) delegateBody []
 1030193                let allVars = vars @ peeled
 2755194                let inner = allVars |> List.fold (fun b v -> Set.add v b) bound
 1030195                let body = capturing block (asUnit (rewriteIn inner body))
 196                // .NET Framework's Expression.Lambda finds `Invoke` by public lookup only, and an F#
 197                // `internal` delegate's is internal: there, the lambda is at the public delegate type
 198                // of the same signature and the delegate is bound over it (#125). Past sixteen
 199                // parameters that stand-in is emitted at run time, which a quotation must not name
 200                // on wasm (see Binders.WideSite) — never reached there: .NET Framework only.
 1030201                let standIn = DelegateMembers.standIn t
 1030202                let literalOf = typedefof<DelegateLiteral<_>>.MakeGenericType t
 1030203                match standIn with
 16204                | Some standIn -> Expr.Call(literalOf.GetMethod("From"), [ Expr.Coerce(Expr.NewDelegate(standIn, allVars
 205                // Re-wrapped so `.Method` is the delegate type's own `Invoke` (see `DelegateLiteral`).
 1014206                | None -> Expr.Call(literalOf.GetMethod("Over"), [ Expr.NewDelegate(t, allVars, body) ])
 13064207            | ShapeLambda(v, lambdaBody) -> Expr.Lambda(v, capturing block (asUnit (rewriteIn (bound.Add v) lambdaBody))
 208            // A void call where a `unit` value is expected (`ignore (list.Add x)`, `f (list.Add x)`):
 209            // the converter has no value for it, so run it, then `()`.
 30889210            | Call(receiver, mi, args) when args |> List.exists isUnitCall ->
 14211                let args' = args |> List.map (fun a -> if isUnitCall a then asUnit (rewrite a) else rewrite a)
 4212                match receiver with
 0213                | Some r -> Expr.Call(rewrite r, mi, args')
 4214                | None -> Expr.Call(mi, args')
 594215            | Application(f, arg) when isUnitCall arg -> Expr.Application(rewrite f, asUnit (rewrite arg))
 61345216            | _ -> Quotation.rebuild rewrite e
 217
 8474218        let delegateType = typedefof<Func<_, _>>.MakeGenericType(closure.Type, resultType)
 219        let compiled =
 8474220            try
 8474221                let rewritten = asUnit (rewriteIn Set.empty (normalize body))
 8348222                let lambda = Expr.NewDelegate(delegateType, [ closure ], rewritten)
 8348223                let linq = LeafExpressionConverter.QuotationToExpression lambda :?> LambdaExpression
 8348224                let hoisted = nameNested block.NameFor (SiteHoister().Visit linq :?> LambdaExpression)
 8348225                match TreeHook.Sink with
 8348226                | null -> ()
 0227                | sink -> sink.Invoke(closureType, hoisted)
 8348228                if closureType.IsValueType then
 229                    // The machine comes by reference (a quotation variable cannot be byref):
 230                    // copy it into the by-value local the body was converted against. A byref
 231                    // cannot be closed over, and the copy is free; the reads are off the local.
 4124232                    let machine = hoisted.Parameters.[0]
 4124233                    let byRef = Expression.Parameter(closureType.MakeByRefType(), "machine")
 4124234                    let readerType = typedefof<DlrReader<_, _>>.MakeGenericType(closureType, resultType)
 4124235                    Expression.Lambda(readerType, Expression.Block(resultType, [ machine ], Expression.Assign(machine, b
 4224236                else Expression.Lambda(hoisted.Type, hoisted.Body, name, hoisted.Parameters).Compile()
 252237            with :? DlrTranslationException -> reraise ()
 238               // A static member resolved here by reflection and missing is the binder's kind of
 239               // error, as it would be at the call for an instance target.
 0240               | :? Microsoft.CSharp.RuntimeBinder.RuntimeBinderException -> reraise ()
 0241               | ex -> raise (DlrTranslationException(sprintf "dlr { } could not compile this body: %s\n%A" ex.Message b
 8422242        { Delegate = compiled }

Methods/Properties

.ctor()
translate(System.String,System.Type,System.Type,Microsoft.FSharp.Quotations.FSharpExpr,System.Type,System.Type,Microsoft.FSharp.Quotations.FSharpExpr)
isCaptured@65(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpVar)
Invoke(Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpVar)
isBuilder@67(FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Quotations.FSharpExpr>)
Invoke(Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Quotations.FSharpExpr>)
rewriteIn@75(Microsoft.FSharp.Quotations.FSharpExpr,FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
tupleVar@90(Microsoft.FSharp.Quotations.FSharpExpr,FSharp.Interop.Dlr.TranslateBlock/Block,Microsoft.FSharp.Collections.FSharpSet`1<Microsoft.FSharp.Quotations.FSharpVar>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
subst@126(Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpVar,System.Reflection.PropertyInfo,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
readCell@141(Microsoft.FSharp.Quotations.FSharpVar)
Invoke(Microsoft.FSharp.Quotations.FSharpVar)
viaCells@142(Microsoft.FSharp.Collections.FSharpList`1<System.Tuple`2<Microsoft.FSharp.Quotations.FSharpVar,Microsoft.FSharp.Quotations.FSharpVar>>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Quotations.FSharpExpr)
unitBranches@177(Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(Microsoft.FSharp.Quotations.FSharpExpr)
peel@188(System.Int32,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>)
Invoke(System.Int32,Microsoft.FSharp.Quotations.FSharpExpr,Microsoft.FSharp.Collections.FSharpList`1<Microsoft.FSharp.Quotations.FSharpVar>)