| | | 1 | | namespace FSharp.Interop.Dlr |
| | | 2 | | |
| | | 3 | | open System |
| | | 4 | | open System.Linq.Expressions |
| | | 5 | | open FSharp.Quotations |
| | | 6 | | open FSharp.Quotations.Patterns |
| | | 7 | | open FSharp.Quotations.DerivedPatterns |
| | | 8 | | open FSharp.Quotations.ExprShape |
| | | 9 | | open FSharp.Reflection |
| | | 10 | | open Microsoft.FSharp.Linq.RuntimeHelpers |
| | | 11 | | open 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. |
| | | 16 | | module 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. |
| | 0 | 30 | | 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: |
| | 26686 | 38 | | let closure = if closureType.IsValueType then Var("sm", closureType) else Var("closure", typeof<obj>) |
| | 13343 | 39 | | let fields = Captures.fields closureType |
| | 13343 | 40 | | let reached, aliases = Captures.reached fields memberBody body |
| | | 41 | | // Only an optimized build substitutes or splits a variable: the state machine, or in Release |
| | | 42 | | // the closure of `(dlr { … } : unit -> R) ()`. |
| | | 43 | | // The F# optimizer ran: it substitutes and captures variables of its own (#280). A Debug |
| | | 44 | | // build that makes state machines (the 8.0, 9.0 and 11.0 compilers) does not. |
| | 13343 | 45 | | let optimizerRan = |
| | 13343 | 46 | | not (closureType.Assembly.GetCustomAttributes(typeof<Diagnostics.DebuggableAttribute>, false) |
| | 33373 | 47 | | |> Seq.exists (fun a -> (a :?> Diagnostics.DebuggableAttribute).IsJITOptimizerDisabled)) |
| | 22314 | 48 | | let optimized = closureType.IsValueType || optimizerRan |
| | 13343 | 49 | | let block = |
| | 13343 | 50 | | { BuilderType = builderType |
| | 13343 | 51 | | Context = context |
| | 13343 | 52 | | MemberBody = memberBody |
| | 13343 | 53 | | ClosureType = closureType |
| | 13343 | 54 | | Closure = closure |
| | 13343 | 55 | | Fields = fields |
| | 13343 | 56 | | Substituted = aliases |
| | 13343 | 57 | | Ambiguous = |
| | 13343 | 58 | | reached |
| | 72458 | 59 | | |> List.countBy (fun v -> v.Name) |> List.filter (fun (_, n) -> n > 1) |> List.map fst |> Set.ofList |
| | 13343 | 60 | | // Only an optimized build splits a tuple (the state machine, or in Release the closure |
| | 13343 | 61 | | // of `(dlr { … } : unit -> R) ()`), and not one it captures whole as a field. |
| | 13343 | 62 | | |> Set.union ( |
| | 37383 | 63 | | if optimized then Captures.elementNameClashes (fun t -> not (fields.ContainsKey t)) memberBody els |
| | 13343 | 64 | | // Another variable of a reached name, which the definition of a value the block |
| | 13343 | 65 | | // reads whole was built from (a tuple, a record, a union case): the optimizer may |
| | 13343 | 66 | | // know an element is that variable and capture it too, named `y`, `y0`, … in an |
| | 13343 | 67 | | // order of its own, so the block's own `y` read by name may get it, whatever the |
| | 13343 | 68 | | // optimizer did to the block's `y` (#280). And a field `y0`, `y1`, … that no |
| | 13343 | 69 | | // reached variable is named, beside a reached `y`: a second `y` captured some |
| | 13343 | 70 | | // other way. Both refused. |
| | 13343 | 71 | | |> Set.union ( |
| | 20030 | 72 | | if not optimizerRan then Set.empty |
| | 13343 | 73 | | else |
| | 18084 | 74 | | let names = reached |> List.map (fun v -> v.Name) |> Set.ofList |
| | 13343 | 75 | | // A reached variable and what it renames, `let y = y` (a `let!` in a task |
| | 13343 | 76 | | // binds its value so): one value under one name. |
| | 13343 | 77 | | let own = |
| | 13343 | 78 | | let rec renames (v: Var) = |
| | 11432 | 79 | | match TranslatePatterns.letDefinition v memberBody with |
| | 75 | 80 | | | Some(Var w) when not v.IsMutable && not w.IsMutable && w.Name = v.Name && w <> v -> |
| | 11428 | 81 | | | _ -> [ v ] |
| | 6656 | 82 | | reached |> List.collect renames |> Set.ofList |
| | 13343 | 83 | | // The variables a reached field's value holds as an element, through |
| | 13343 | 84 | | // immutable lets, projections and tuple, record and union-case |
| | 13343 | 85 | | // construction: what the optimizer can take back out of it. A variable a |
| | 13343 | 86 | | // computation used (`y * 2`) it cannot. |
| | 13343 | 87 | | let rec elementsOf (seen: Set<Var>) (e: Expr) : Set<Var> = |
| | 4770 | 88 | | match e with |
| | 1901 | 89 | | | Var v when not v.IsMutable && not (seen.Contains v) -> |
| | 1897 | 90 | | let seen = seen.Add v |
| | 13343 | 91 | | // A local function's parameter is its one call's argument. |
| | 1897 | 92 | | match TranslatePatterns.letDefinition v memberBody |> Option.orElse (TranslatePatterns |
| | 1660 | 93 | | | Some d -> elementsOf seen d |
| | 237 | 94 | | | None -> seen |
| | 669 | 95 | | | NewTuple es | NewRecord(_, es) | NewUnionCase(_, es) -> List.fold elementsOf seen es |
| | 378 | 96 | | | PropertyGet(Some inner, _, []) | TupleGet(inner, _) | Coerce(inner, _) -> elementsOf see |
| | 1826 | 97 | | | _ -> seen |
| | 13343 | 98 | | // Whether the block's compiled code takes `r` apart, where the optimizer |
| | 13343 | 99 | | // can put an element's variable in its place: anything but `r` whole as |
| | 13343 | 100 | | // a dynamic call's target or argument (boxed or not), which it passes as |
| | 13343 | 101 | | // it is. One the block reaches only through a definition counts as apart. |
| | 13343 | 102 | | let apart (r: Var) = |
| | 13343 | 103 | | let rec go (whole: bool) (e: Expr) = |
| | 42641 | 104 | | match e with |
| | 19821 | 105 | | | Var v -> v = r && not whole |
| | 13343 | 106 | | | MemberOp(InvokeMember(target, name, args)) -> |
| | 4224 | 107 | | go true target || go false name |
| | 2061 | 108 | | || (match args with |
| | 863 | 109 | | | NewTuple items -> List.exists (go true) items |
| | 1198 | 110 | | | a -> go true a) |
| | 2407 | 111 | | | MemberOp(GetMember(target, name)) -> go true target || go false name |
| | 396 | 112 | | | MemberOp(SetMember(target, name, value)) -> go true target || go false name || go tr |
| | 204 | 113 | | | Coerce(inner, _) -> go whole inner |
| | 2105 | 114 | | | Call(None, mi, [ inner ]) when mi.DeclaringType.FullName = "Microsoft.FSharp.Core.Op |
| | 0 | 115 | | | ShapeVar _ -> false |
| | 4346 | 116 | | | ShapeLambda(_, b) -> go false b |
| | 19033 | 117 | | | ShapeCombination(_, es) -> List.exists (go false) es |
| | 4269 | 118 | | not (body.GetFreeVars() |> Seq.contains r) || go false body |
| | 13343 | 119 | | // A reached variable read from fields: its own, or its elements' (a split |
| | 13343 | 120 | | // tuple). |
| | 13343 | 121 | | let held (r: Var) = |
| | 25546 | 122 | | fields.ContainsKey r.Name || (Captures.elementField fields r 0).IsSome || (Captures.elemen |
| | 13343 | 123 | | // Of another type, a field holding it is refused by `Captures.read`'s |
| | 13343 | 124 | | // type check, never read (a match's own `matchValue` beside a user's, |
| | 13343 | 125 | | // #281 corpus); a ref cell of the type is read through. |
| | 13343 | 126 | | let readAs (w: Var) = |
| | 357 | 127 | | reached |> List.exists (fun v -> |
| | 1011 | 128 | | v.Name = w.Name |
| | 411 | 129 | | && (v.Type = w.Type || (w.Type.IsGenericType && w.Type.GetGenericTypeDefinition() = ty |
| | 6656 | 130 | | let flowsIn = |
| | 24740 | 131 | | set [ for r in reached do |
| | 27246 | 132 | | if held r && apart r then |
| | 6814 | 133 | | for w in elementsOf Set.empty (Expr.Var r) do |
| | 11206 | 134 | | if w <> r && not (own.Contains w) && readAs w then yield w.Name ] |
| | 6656 | 135 | | let secondCapture = |
| | 24722 | 136 | | set [ for n in names do |
| | 22820 | 137 | | if fields.Keys |> Seq.exists (fun f -> |
| | 12975 | 138 | | f.Length > n.Length && f.StartsWith(n, StringComparison.Ordinal) |
| | 81 | 139 | | && f |> Seq.skip n.Length |> Seq.forall Char.IsDigit |
| | 22847 | 140 | | && not (names.Contains f)) then |
| | 6674 | 141 | | yield n ] |
| | 13343 | 142 | | Set.union flowsIn secondCapture) |
| | 13343 | 143 | | Name = name |
| | 13343 | 144 | | Names = Collections.Generic.Dictionary() } |
| | | 145 | | |
| | | 146 | | /// Variables bound inside the expression being rewritten are left alone; anything else |
| | | 147 | | /// that is not the builder comes from the closure or the enclosing member. |
| | 43814 | 148 | | let isCaptured (bound: Set<Var>) (v: Var) = not (bound.Contains v) && v.Type <> block.BuilderType |
| | | 149 | | |
| | | 150 | | let isBuilder (receiver: Expr option) = |
| | 64349 | 151 | | match receiver with |
| | 24404 | 152 | | | Some r -> r.Type = block.BuilderType |
| | 39945 | 153 | | | None -> false |
| | | 154 | | |
| | | 155 | | /// Builder calls and marker operations go to their sections; everything else is generic |
| | | 156 | | /// rewriting: captured variables become closure reads, and the structure is rebuilt as-is |
| | | 157 | | /// except where the expression tree has no form for it. |
| | | 158 | | let rec rewriteIn (bound: Set<Var>) (e: Expr) : Expr = |
| | 238112 | 159 | | let rewrite = rewriteIn bound |
| | 238112 | 160 | | match e with |
| | 85556 | 161 | | | Call(receiver, mi, args) when isBuilder receiver -> Plumbing.call block rewriteIn bound mi args e |
| | | 162 | | | _ -> |
| | 216905 | 163 | | match Members.tryOperation block rewriteIn bound e with |
| | 15784 | 164 | | | Some rewritten -> rewritten |
| | | 165 | | | None -> |
| | | 166 | | // A mutable struct captured from outside the block, mutated in place (see `inPlace`). |
| | 202594 | 167 | | match inPlace (fun v -> isCaptured bound v && Captures.isCell block v) (Captures.read block rewrite) (Captur |
| | 42 | 168 | | | Some written -> written |
| | | 169 | | | None -> |
| | | 170 | | // An element of a captured tuple the optimizer split into a field per element, read |
| | | 171 | | // directly or through what it inlines: a local function applied to unit, or an |
| | | 172 | | // immutable alias of an immutable variable (that value) whose name is no other's. An |
| | | 173 | | // alias with a field of its own is read through it: a field `y_0` beside it is then a |
| | | 174 | | // user's variable, not the split of `y` (#267). |
| | | 175 | | let rec tupleVar (e: Expr) : Var option = |
| | 2751 | 176 | | match e with |
| | 2976 | 177 | | | Var v when isCaptured bound v && FSharp.Reflection.FSharpType.IsTuple v.Type -> |
| | 231 | 178 | | match letDefinition v memberBody with |
| | 48 | 179 | | | Some(Var y as d) when block.Substituted.Contains v || (not v.IsMutable && not y.IsMutable && not ( |
| | 0 | 180 | | tupleVar d |> Option.orElse (Some v) |
| | 231 | 181 | | | _ -> Some v |
| | 18 | 182 | | | Application(Var f, Value(_, t)) when t = typeof<unit> && isCaptured bound f && not (block.Fields.Conta |
| | 3 | 183 | | match letDefinition f memberBody with |
| | 3 | 184 | | | Some(Lambda(_, b)) -> tupleVar b |
| | 0 | 185 | | | _ -> None |
| | 2517 | 186 | | | _ -> None |
| | | 187 | | let element = |
| | 200884 | 188 | | match e with |
| | | 189 | | | TupleGet(inner, i) -> |
| | 3786 | 190 | | tupleVar inner |> Option.bind (fun v -> Captures.element block v i) |
| | 15002 | 191 | | | Call(None, mi, [ inner ]) when mi.DeclaringType.FullName = "Microsoft.FSharp.Core.Operators" && (mi.Na |
| | 1038 | 192 | | tupleVar inner |> Option.bind (fun v -> Captures.element block v (if mi.Name = "Fst" then 0 else 1)) |
| | 198136 | 193 | | | _ -> None |
| | 200875 | 194 | | match element with |
| | 30 | 195 | | | Some read -> read |
| | | 196 | | | None -> |
| | 200845 | 197 | | match e with |
| | 72365 | 198 | | | Var v when isCaptured bound v -> Captures.read block (rewriteIn bound) v |
| | 390 | 199 | | | VarSet(v, value) when isCaptured bound v -> Captures.assign block v (rewrite value) |
| | 52755 | 200 | | | ShapeVar _ -> e |
| | | 201 | | // A resumable-code-typed leftover of the builder's shape (see `codeType`): the |
| | | 202 | | // compiler's `null` after the rethrow in an unmatched `try … with` arm. |
| | 3711 | 203 | | | Value(null, t) when isCode t -> defaultOf (codeType t) |
| | | 204 | | // A `let mutable` of the block has no expression-tree form: loop and try bodies are |
| | | 205 | | // compiled into delegates, and a tree variable cannot be assigned from inside one. So |
| | | 206 | | // it lives in a reference cell, as the compiler does for a captured mutable, with |
| | | 207 | | // reads and writes going through the cell. |
| | 20322 | 208 | | | Let(v, def, letBody) when v.IsMutable -> |
| | 2722 | 209 | | let cell = Var(v.Name, typedefof<Ref<_>>.MakeGenericType v.Type) |
| | 2722 | 210 | | let value = cell.Type.GetProperty("Value") |
| | | 211 | | let rec subst (e: Expr) = |
| | 77806 | 212 | | match inPlace ((=) v) (fun _ -> Expr.PropertyGet(Expr.Var cell, value)) (fun _ x -> Expr.PropertySet |
| | 1440 | 213 | | | Some written -> written |
| | | 214 | | | None -> |
| | 73486 | 215 | | match e with |
| | 3872 | 216 | | | VarSet(v', x) when v' = v -> Expr.PropertySet(Expr.Var cell, value, subst x) |
| | 26596 | 217 | | | Var v' when v' = v -> Expr.PropertyGet(Expr.Var cell, value) |
| | 67762 | 218 | | | _ -> Quotation.rebuild subst e |
| | 2722 | 219 | | Expr.Let(cell, Expr.NewObject(cell.Type.GetConstructor([| v.Type |]), [ rewrite def ]), rewriteIn (bound |
| | 17600 | 220 | | | Let(v, def, letBody) -> Expr.Let(v, rewrite def, rewriteIn (bound.Add v) letBody) |
| | | 221 | | // `let rec` has no expression-tree form; tie the knot through reference cells, as the |
| | | 222 | | // compiler does: each binding becomes a cell, uses read the cell, and the definitions |
| | | 223 | | // are assigned after all cells exist so mutual recursion works too. |
| | | 224 | | | LetRecursive(bindings, letBody) -> |
| | 930 | 225 | | let cells = [ for (v, _) in bindings -> v, Var(v.Name + "'", typedefof<Ref<_>>.MakeGenericType v.Type) ] |
| | 375 | 226 | | let readCell (cell: Var) = Expr.PropertyGet(Expr.Var cell, cell.Type.GetProperty("Value")) |
| | 5856 | 227 | | let viaCells (e: Expr) = e.Substitute(fun v -> cells |> List.tryFind (fun (rv, _) -> rv = v) |> Option.m |
| | 459 | 228 | | let bound = cells |> List.fold (fun (b: Set<Var>) (_, c) -> b.Add c) bound |
| | 180 | 229 | | let assignments = |
| | 555 | 230 | | [ for (_, def), (_, cell) in List.zip bindings cells -> |
| | 375 | 231 | | Expr.PropertySet(Expr.Var cell, cell.Type.GetProperty("Value"), rewriteIn bound (viaCells def)) |
| | 180 | 232 | | let body = rewriteIn bound (viaCells letBody) |
| | 279 | 233 | | let inner = List.foldBack (fun assign rest -> Expr.Sequential(assign, rest)) assignments body |
| | 180 | 234 | | List.foldBack |
| | 180 | 235 | | (fun (v: Var, cell: Var) rest -> |
| | 195 | 236 | | Expr.Let(cell, Expr.NewObject(cell.Type.GetConstructor [| v.Type |], [ Expr.Value(null, v.Type) |
| | 180 | 237 | | cells inner |
| | | 238 | | // A `try` that is not the builder's (under a lambda, inside a delegate literal, or a |
| | | 239 | | // `try … with` used as a value): LeafExpressionConverter has no form for it, so it runs |
| | | 240 | | // through the same delegates as the block's own (#158). F#'s filter only repeats the |
| | | 241 | | // handler's match, which ends in `reraise ()` where no case fits; the handler alone does. |
| | | 242 | | | TryWith(body, _, _, ex, handler) -> |
| | 384 | 243 | | let func = Plumbing.func block rewriteIn bound |
| | 768 | 244 | | Expr.Call(tryWith.MakeGenericMethod(e.Type), [ func [] body; func [ ex ] (Plumbing.rethrowing ex handler |
| | | 245 | | // `while` and `for i in a .. b` that are not the builder's (under a lambda, in a delegate |
| | | 246 | | // literal, inside an expression): no LINQ form either, so the same delegates (#162). |
| | | 247 | | | WhileLoop(guard, body) -> |
| | 72 | 248 | | let func = Plumbing.func block rewriteIn bound |
| | 144 | 249 | | Expr.Call(whileLoop, [ func [] guard; func [] body ]) |
| | | 250 | | | ForIntegerRangeLoop(i, low, high, body) -> |
| | 75 | 251 | | let func = Plumbing.func block rewriteIn bound |
| | 150 | 252 | | Expr.Call(forRange, [ rewrite low; rewrite high; func [ i ] body ]) |
| | 272 | 253 | | | PrimitiveRange(typed, args) -> Expr.Call(typed, List.map rewrite args) |
| | | 254 | | // An element of a struct tuple: the converter's TupleGet looks for a reference tuple's |
| | | 255 | | // property and rejects a ValueTuple's index (#162), so it is the field itself |
| | | 256 | | // (`Item1`…`Item7`, then `Rest`), as the byref holder reads it. |
| | 2520 | 257 | | | TupleGet(tuple, index) when tuple.Type.IsValueType -> |
| | 720 | 258 | | Binders.byRefHolderPath tuple.Type index |> List.fold (fun e f -> Expr.FieldGet(e, f)) (rewrite tuple) |
| | | 259 | | | TryFinally(body, compensation) -> |
| | 150 | 260 | | let func = Plumbing.func block rewriteIn bound |
| | | 261 | | // `use`'s compensation is `if (d :? IDisposable) then d.Dispose() else ()`: a void |
| | | 262 | | // call and `()` as the branches, which the converter rejects as mismatched. |
| | | 263 | | let rec unitBranches (c: Expr) = |
| | 600 | 264 | | match c with |
| | 300 | 265 | | | IfThenElse(cond, a, b) when c.Type = typeof<unit> -> Expr.IfThenElse(cond, unitBranches a, unitBra |
| | 600 | 266 | | | _ when isUnitCall c -> Expr.Sequential(c, Expr.Value(())) |
| | 300 | 267 | | | _ -> c |
| | 300 | 268 | | Expr.Call(tryFinally.MakeGenericMethod(e.Type), [ func [] body; func [] (unitBranches compensation) ]) |
| | | 269 | | | NewDelegate(t, vars, delegateBody) -> |
| | | 270 | | // A delegate literal's lambda is the delegate itself, not an F# function: keep it |
| | | 271 | | // whole (on wasm, made capturing: see `capturing`). The quotation may give the |
| | | 272 | | // parameters as nested lambdas in the body rather than in `vars`. |
| | 1546 | 273 | | let n = (DelegateMembers.invokeOf t).GetParameters().Length |
| | | 274 | | let rec peel k (e: Expr) acc = |
| | 1546 | 275 | | match e with |
| | 0 | 276 | | | Lambda(v, b) when k > 0 -> peel (k - 1) b (v :: acc) |
| | 1546 | 277 | | | _ -> List.rev acc, e |
| | 1546 | 278 | | let peeled, body = peel (n - vars.Length) delegateBody [] |
| | 1546 | 279 | | let allVars = vars @ peeled |
| | 4135 | 280 | | let inner = allVars |> List.fold (fun b v -> Set.add v b) bound |
| | 1546 | 281 | | let body = capturing block (asUnit (rewriteIn inner body)) |
| | | 282 | | // .NET Framework's Expression.Lambda finds `Invoke` by public lookup only, and an F# |
| | | 283 | | // `internal` delegate's is internal: there, the lambda is at the public delegate type |
| | | 284 | | // of the same signature and the delegate is bound over it (#125). Past sixteen |
| | | 285 | | // parameters that stand-in is emitted at run time, which a quotation must not name |
| | | 286 | | // on wasm (see Binders.WideSite) — never reached there: .NET Framework only. |
| | 1546 | 287 | | let standIn = DelegateMembers.standIn t |
| | 1546 | 288 | | let literalOf = typedefof<DelegateLiteral<_>>.MakeGenericType t |
| | 1546 | 289 | | match standIn with |
| | 16 | 290 | | | Some standIn -> Expr.Call(literalOf.GetMethod("From"), [ Expr.Coerce(Expr.NewDelegate(standIn, allVars |
| | | 291 | | // Re-wrapped so `.Method` is the delegate type's own `Invoke` (see `DelegateLiteral`). |
| | 1530 | 292 | | | None -> Expr.Call(literalOf.GetMethod("Over"), [ Expr.NewDelegate(t, allVars, body) ]) |
| | 19974 | 293 | | | ShapeLambda(v, lambdaBody) -> Expr.Lambda(v, capturing block (asUnit (rewriteIn (bound.Add v) lambdaBody)) |
| | | 294 | | // A void call where a `unit` value is expected (`ignore (list.Add x)`, `f (list.Add x)`): |
| | | 295 | | // the converter has no value for it, so run it, then `()`. |
| | 47629 | 296 | | | Call(receiver, mi, args) when args |> List.exists isUnitCall -> |
| | 21 | 297 | | let args' = args |> List.map (fun a -> if isUnitCall a then asUnit (rewrite a) else rewrite a) |
| | 6 | 298 | | match receiver with |
| | 0 | 299 | | | Some r -> Expr.Call(rewrite r, mi, args') |
| | 6 | 300 | | | None -> Expr.Call(mi, args') |
| | 1005 | 301 | | | Application(f, arg) when isUnitCall arg -> Expr.Application(rewrite f, asUnit (rewrite arg)) |
| | 94074 | 302 | | | _ -> Quotation.rebuild rewrite e |
| | | 303 | | |
| | 13343 | 304 | | let delegateType = typedefof<Func<_, _>>.MakeGenericType(closure.Type, resultType) |
| | | 305 | | let compiled = |
| | 13343 | 306 | | try |
| | 13343 | 307 | | let rewritten = asUnit (rewriteIn Set.empty (normalize body)) |
| | 13109 | 308 | | let lambda = Expr.NewDelegate(delegateType, [ closure ], rewritten) |
| | 13109 | 309 | | let linq = LeafExpressionConverter.QuotationToExpression lambda :?> LambdaExpression |
| | 13109 | 310 | | let hoisted = nameNested block.NameFor (SiteHoister().Visit linq :?> LambdaExpression) |
| | 13109 | 311 | | match TreeHook.Sink with |
| | 13103 | 312 | | | null -> () |
| | 6 | 313 | | | sink -> sink.Invoke(closureType, hoisted) |
| | 13103 | 314 | | if closureType.IsValueType then |
| | | 315 | | // The machine comes by reference (a quotation variable cannot be byref): |
| | | 316 | | // copy it into the by-value local the body was converted against. A byref |
| | | 317 | | // cannot be closed over, and the copy is free; the reads are off the local. |
| | 4270 | 318 | | let machine = hoisted.Parameters.[0] |
| | 4270 | 319 | | let byRef = Expression.Parameter(closureType.MakeByRefType(), "machine") |
| | 4270 | 320 | | let readerType = typedefof<DlrReader<_, _>>.MakeGenericType(closureType, resultType) |
| | 4270 | 321 | | Expression.Lambda(readerType, Expression.Block(resultType, [ machine ], Expression.Assign(machine, b |
| | 8833 | 322 | | else Expression.Lambda(hoisted.Type, hoisted.Body, name, hoisted.Parameters).Compile() |
| | 474 | 323 | | with :? DlrTranslationException -> reraise () |
| | | 324 | | // A static member resolved here by reflection and missing is the binder's kind of |
| | | 325 | | // error, as it would be at the call for an instance target. |
| | 0 | 326 | | | :? Microsoft.CSharp.RuntimeBinder.RuntimeBinderException -> reraise () |
| | 6 | 327 | | | ex -> raise (DlrTranslationException(sprintf "dlr { } could not compile this body: %s\n%A" ex.Message b |
| | 13262 | 328 | | { Delegate = compiled } |