< Summary - FSharp.Interop.Dlr

Information
Class: FSharp.Interop.Dlr.Binders
Assembly: FSharp.Interop.Dlr
File(s): /home/runner/work/FSharp.Interop.Dlr/FSharp.Interop.Dlr/FSharp.Interop.Dlr/Binders.fs
Line coverage
100%
Covered lines: 218
Uncovered lines: 0
Coverable lines: 218
Total lines: 493
Line coverage: 100%
Branch coverage
95%
Covered branches: 190
Total branches: 198
Branch coverage: 95.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
dynamicArg(...)100%11100%
staticTarget(...)100%11100%
isStatic(...)100%11100%
callsOnly(...)100%22100%
typedArg(...)100%22100%
withFlag(...)100%11100%
constant(...)100%11100%
byRefArg(...)100%22100%
named(...)100%11100%
argInfo(...)100%11100%
resultFlags(...)100%22100%
allPositional(...)100%11100%
site(...)75%44100%
wideMethod(...)100%11100%
byRefHolderType(...)100%11100%
byRefHolderPath(...)100%11100%
byRefHolderOf(...)100%44100%
byRefSite(...)95.83%2424100%
invokeMemberByRef(...)100%66100%
invokeByRef(...)100%66100%
invokeConstructorByRef(...)100%22100%
siteCall(...)87.5%1616100%
getMember(...)100%11100%
setMember(...)100%11100%
convertBinder(...)100%44100%
smartInvokeMember(...)100%1616100%
invokeMemberOrApply(...)100%22100%
invokeConstructor(...)100%44100%
invokeOrApply(...)100%66100%
asFunction(...)96.66%3030100%
domains@283(...)100%22100%
Invoke(...)100%22100%
memberInvoker(...)100%22100%
functionMember(...)100%11100%
delegateRead(...)90%2020100%
delegateMember(...)100%22100%
invoke@396-1(...)100%11100%
Invoke(...)100%11100%
functionTarget(...)100%11100%
getIndex(...)100%22100%
setIndex(...)100%22100%
compoundAssign(...)100%44100%
binaryOperation(...)100%22100%
unaryOperation(...)100%11100%
unaryOperation(...)100%11100%
convert(...)100%44100%
convertExplicit(...)100%11100%

File(s)

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

#LineLine coverage
 1namespace FSharp.Interop.Dlr
 2
 3open System
 4open System.Diagnostics.CodeAnalysis
 5open System.Dynamic
 6open System.Linq.Expressions
 7open System.Reflection
 8open System.Runtime.CompilerServices
 9open Microsoft.CSharp.RuntimeBinder
 10open FSharp.Quotations
 11
 12/// Builds Microsoft.CSharp binders and emits the quotation fragment that calls a
 13/// pre-created CallSite: `Call(FieldGet(Value site, Target), Invoke, site :: args)`.
 14/// The `Value site` becomes an Expression.Constant, so the site is baked into the
 15/// compiled delegate.
 16module internal Binders =
 17
 18    /// `voidType` makes the F# compiler emit IL the JIT rejects, so get it indirectly.
 19    let private voidType = typeof<Action>.GetMethod("Invoke").ReturnType
 20
 21    /// One argument as the binder sees it: its expression, the static type used in
 22    /// the site delegate, and the C# argument-info flags.
 23    type Arg =
 24        { Expr: Expr
 25          Type: Type
 26          Flags: CSharpArgumentInfoFlags
 27          Name: string
 28          /// A `Dlr.ref`'s variable: two refs over the same one are one storage at the call, as in C#.
 29          Alias: obj }
 30
 31    /// A dynamically typed argument: the binder dispatches on its runtime type.
 32    let dynamicArg (e: Expr) =
 1100033        { Expr = e; Type = typeof<obj>; Flags = CSharpArgumentInfoFlags.None; Name = null; Alias = null }
 34
 35    /// A type as the target (`Dlr.Static<T>.Overloads`, `Dlr.new'<T>`): argument 0 of the site is
 36    /// `typeof<T>` flagged as a static type, the C# compiler's shape for `T.Member(…)`.
 37    let staticTarget (t: Type) =
 22038        { Expr = Expr.Value(t, typeof<Type>); Type = typeof<Type>; Flags = CSharpArgumentInfoFlags.UseCompileTimeType ||
 39
 655940    let isStatic (a: Arg) = a.Flags.HasFlag CSharpArgumentInfoFlags.IsStaticType
 41
 42    /// `Dlr.Static<T>.Overloads` is for calls; C#'s binder has no static form of the other operations
 43    /// (GetMember, SetMember, IsEvent, indexers) and plain F# already has them: `T.P`.
 44    let private callsOnly (what: string) (target: Arg) =
 978945        if isStatic target then
 2446            raise (DlrTranslationException(sprintf "dlr { } does not support %s on Dlr.Static<T>.Overloads, which is for
 47
 48    /// A statically typed argument: the binder uses the quotation's type, as C# would.
 49    /// An `obj`-typed expression stays dynamic so F# callers get FSharp.Interop.Dynamic-like
 50    /// overload resolution on boxed values.
 51    let typedArg (e: Expr) =
 725452        if e.Type = typeof<obj> then dynamicArg e
 580253        else { Expr = e; Type = e.Type; Flags = CSharpArgumentInfoFlags.UseCompileTimeType; Name = null; Alias = null }
 54
 55    let private withFlag (flag: CSharpArgumentInfoFlags) (arg: Arg) =
 56        // Spelled out with int locals: `|||` straight on the enum resolved to the dynamic
 57        // (throwing) FSharp.Core path when compiled against the FSharp.Core floor.
 605758        let current: int = LanguagePrimitives.EnumToValue arg.Flags
 605759        let added: int = LanguagePrimitives.EnumToValue flag
 605760        { arg with Flags = LanguagePrimitives.EnumOfValue(current ||| added) }
 61
 62    /// A literal argument: the binder applies C#'s constant conversions (an in-range `int`
 63    /// literal to `byte`, `0` to an enum, `null` to any reference type).
 328164    let constant (arg: Arg) = withFlag CSharpArgumentInfoFlags.Constant arg
 65
 66    /// A `ref` / `out` argument (`Dlr.ref v`, `Dlr.out`): the site's parameter is `t&`, and
 67    /// `initial` is the value passed in (null for an out); `alias` is a ref's variable (null for an out).
 68    let byRefArg (isOut: bool) (t: Type) (initial: Expr) (alias: obj) =
 438669        withFlag (if isOut then CSharpArgumentInfoFlags.IsOut else CSharpArgumentInfoFlags.IsRef)
 146270            { Expr = initial; Type = t.MakeByRefType(); Flags = CSharpArgumentInfoFlags.UseCompileTimeType; Name = null;
 71
 72    let named (name: string) (arg: Arg) =
 262873        { withFlag CSharpArgumentInfoFlags.NamedArgument arg with Name = name }
 74
 1739975    let private argInfo (a: Arg) = CSharpArgumentInfo.Create(a.Flags, a.Name)
 76
 77    /// C#'s flags for a call whose result is discarded (a statement) or used.
 524078    let private resultFlags (discard: bool) = if discard then CSharpBinderFlags.ResultDiscarded else CSharpBinderFlags.N
 79
 80    /// No argument is named: the positional F# rules apply (a named call is C#'s alone).
 1199481    let private allPositional (args: Arg list) = args |> List.forall (fun a -> isNull a.Name)
 82
 83    /// A `CallSite<_>` for `binder` over `args`, as a `Value` node (a constant in the compiled tree).
 84    let private site (binder: CallSiteBinder) (args: Arg list) (resultType: Type) =
 2281985        let delegateType =
 9908886            Expression.GetDelegateType(Array.ofList (typeof<CallSite> :: [ for a in args -> a.Type ] @ [ resultType ]))
 2281987        let siteType = typedefof<CallSite<_>>.MakeGenericType delegateType
 2281988        Expr.Value(siteType.GetMethod("Create").Invoke(null, [| box binder |]), siteType)
 89
 90    /// An in-place operation on a struct copy, then its write-back (`inPlace` in `TranslateBlock.fs`): the
 91    /// write-back must run even when the operation throws after mutating, as plain F# keeps the
 92    /// mutation — a `finally`, which the quotation converter has no form for. This placeholder is
 93    /// what the translator emits; the LINQ `SiteHoister` rewrites it into a TryFinally. The body is
 94    /// the slow path, for a tree the hoister has not seen (the write-back then only on success);
 95    /// the hoister sees every tree, so the body never runs and is excluded from coverage.
 96    type InPlace =
 97        [<ExcludeFromCodeCoverage>]
 98        static member Then<'T>(operation: 'T, writeBack: unit) : 'T = ignore writeBack; operation
 99    /// Past Func's 17 type parameters (15 arguments and up) a site's delegate is a type emitted at run time,
 100    /// which must not be named in a quotation: FSharp.Core's checks ask its assembly
 101    /// `ReflectionOnly`, unimplemented on browser-wasm. Such a call is written as one of these
 102    /// placeholders — every type in it a plain one — and the LINQ `SiteHoister` (`SiteHoister.fs`)
 103    /// rewrites it into the typed `Invoke` after conversion. The bodies are the slow path, for a
 104    /// tree the hoister has not seen; the hoister sees every tree, so they never run and are
 105    /// excluded from coverage.
 106    [<ExcludeFromCodeCoverage>]
 107    let private wideInvoke (site: CallSite) (args: obj[]) =
 108        let target = site.GetType().GetField("Target").GetValue site :?> Delegate
 109        target.DynamicInvoke(Array.append [| box site |] args)
 110    [<ExcludeFromCodeCoverage>]
 111    type WideSite =
 112        static member Invoke(site: CallSite, delegateType: Type, args: obj[]) : obj = ignore delegateType; wideInvoke si
 113        static member InvokeVoid(site: CallSite, delegateType: Type, args: obj[]) : unit = ignore delegateType; wideInvo
 4114    let private wideMethod (name: string) = typeof<WideSite>.GetMethod(name, BindingFlags.Static ||| BindingFlags.Public
 115    let private wideInvokeMethod = wideMethod "Invoke"
 116    let private wideInvokeVoidMethod = wideMethod "InvokeVoid"
 117
 118    /// The holder a byref call returns: the result (`obj`), then each byref argument's value after
 119    /// the call, typed, in argument order — a `ValueTuple`, nested in its `Rest` past seven, so
 120    /// nothing is boxed or allocated on the way out.
 657121    let byRefHolderType (types: Type list) : Type = Tuples.valueTupleOf types
 122    /// Element `i` of a holder: `Item(i+1)`, through `Rest` past the seventh.
 1418123    let byRefHolderPath (holder: Type) (i: int) : FieldInfo list = Tuples.fieldPath holder i
 124
 125    /// A site with byref parameters (`Dlr.out`, `Dlr.ref`). No quotation can pass a byref, so the
 126    /// call is this placeholder: the values go in as an `obj[]` (target first) and come back as the
 127    /// holder `'H` (see `byRefHolderType`). `byRefs` are the byref positions in `args`, `outs` those
 128    /// of them that are out (whose value in is not read: it starts as the default), and `sameAs`
 129    /// per position the earlier one over the same variable (-1 if none), which shares its storage. The LINQ
 130    /// `SiteHoister` rewrites it into the typed `Invoke` (no array, no boxing); this body is the
 131    /// slow path, correct on the JIT (`DynamicInvoke` writes byref parameters back into its array)
 132    /// but not on Mono wasm, which is why the rewrite is required there. The hoister sees every
 133    /// tree, so the body never runs and is excluded from coverage.
 134    [<ExcludeFromCodeCoverage>]
 135    type ByRefSite =
 136        static member Invoke<'H>(site: CallSite, delegateType: Type, args: obj[], byRefs: int[], outs: int[], sameAs: in
 137            ignore (delegateType, outs, sameAs)
 138            let target = site.GetType().GetField("Target").GetValue site :?> Delegate
 139            let all = Array.append [| box site |] args
 140            let result =
 141                // A binder's error, or the callee's, arrives as itself, as at any other site.
 142                try target.DynamicInvoke all
 143                with :? TargetInvocationException as e when not (isNull e.InnerException) ->
 144                    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(e.InnerException).Throw()
 145                    null
 146            let values = result :: [ for i in byRefs -> all.[i + 1] ]
 147            Tuples.make typeof<'H> values :?> 'H
 148    let private byRefInvokeMethod = typeof<ByRefSite>.GetMethod("Invoke", BindingFlags.Static ||| BindingFlags.Public ||
 149
 150    /// The holder type of a byref call over `all` (target first): the result, then each byref's type.
 17724151    let byRefHolderOf (all: Arg list) = byRefHolderType (typeof<obj> :: [ for a in all do if a.Type.IsByRef then yield a
 152
 153    /// A byref site (see `ByRefSite`) for `binder` over `all`, returning `resultType` (`Void` for a
 154    /// discarded result); the expression is the holder of result and byref values.
 155    let private byRefSite (binder: CallSiteBinder) (all: Arg list) (resultType: Type) : Expr =
 7910156        let delegateType = Expression.GetDelegateType(Array.ofList (typeof<CallSite> :: [ for a in all -> a.Type ] @ [ r
 1294157        let site = typedefof<CallSite<_>>.MakeGenericType(delegateType).GetMethod("Create").Invoke(null, [| box binder |
 17448158        let byRefs = [| for i, a in List.indexed all do if a.Type.IsByRef then yield i |]
 17378159        let outs = [| for i, a in List.indexed all do if a.Flags.HasFlag CSharpArgumentInfoFlags.IsOut then yield i |]
 160        // Per position: the first earlier position whose ref is over the same variable, else -1.
 1294161        let sameAs =
 6616162            [| for i, a in List.indexed all ->
 7986163                if isNull a.Alias then -1
 1645164                else match all |> List.take i |> List.tryFindIndex (fun b -> obj.ReferenceEquals(b.Alias, a.Alias)) with
 1294165        let holder = byRefHolderOf all
 15966166        let boxed = [ for a in all -> if a.Expr.Type = typeof<obj> then a.Expr else Expr.Coerce(a.Expr, typeof<obj>) ]
 1294167        Expr.Call(byRefInvokeMethod.MakeGenericMethod holder,
 1294168                  [ Expr.Value(site, typeof<CallSite>); Expr.Value(delegateType, typeof<Type>)
 1294169                    Expr.NewArray(typeof<obj>, boxed); Expr.Value(byRefs); Expr.Value(outs); Expr.Value(sameAs) ])
 170
 171    /// `x?M(…, Dlr.out, Dlr.ref v, …)`: C#'s InvokeMember over byref parameters (target first in `all`).
 172    let invokeMemberByRef (context: Type) (name: string) (typeArgs: Type list) (discard: bool) (all: Arg list) : Expr =
 1218173        let flags = resultFlags discard
 2436174        let typeArgSeq = match typeArgs with [] -> null | ts -> ts :> seq<Type>
 9874175        byRefSite (Binder.InvokeMember(flags, name, typeArgSeq, context, [ for a in all -> argInfo a ])) all (if discard
 176
 177    /// `Dlr.call f (…, Dlr.out, …)` / `Dlr.apply`: C#'s Invoke of the value itself over byref parameters.
 178    let invokeByRef (context: Type) (discard: bool) (all: Arg list) : Expr =
 68179        callsOnly "invoking a value (Dlr.call / Dlr.apply)" (List.head all)
 68180        let flags = resultFlags discard
 1000181        byRefSite (FSharpByRefInvokeBinder(Binder.Invoke(flags, context, [ for a in all -> argInfo a ]) :?> InvokeBinder
 182
 183    /// `Dlr.new'<T>(…, Dlr.out, …)`: C#'s InvokeConstructor over byref parameters; the result is typed `t`.
 184    let invokeConstructorByRef (context: Type) (t: Type) (args: Arg list) : Expr =
 8185        let all = staticTarget t :: args
 48186        byRefSite (Binder.InvokeConstructor(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ])) all t
 187
 188    /// Emits the call-site invocation for `binder` over `args`, returning `resultType`
 189    /// (`Void` for a discarded result, which yields an Action-shaped site).
 190    let siteCall (binder: CallSiteBinder) (args: Arg list) (resultType: Type) : Expr =
 21931191        let siteExpr = site binder args resultType
 43862192        let siteValue = match siteExpr with FSharp.Quotations.Patterns.Value(v, _) -> v | _ -> null
 21931193        let siteType = siteExpr.Type
 21931194        let siteDelegate = siteType.GetGenericArguments().[0]
 195        // `args` holds the target too; the site's delegate takes the CallSite before them.
 21931196        if DelegateMembers.funcFits (args.Length + 1) then
 21855197            let target = Expr.FieldGet(siteExpr, siteType.GetField("Target"))
 92532198            Expr.Call(target, siteDelegate.GetMethod("Invoke"), siteExpr :: [ for a in args -> a.Expr ])
 199        else
 76200            let isVoid = resultType = typeof<Action>.GetMethod("Invoke").ReturnType
 4500201            let boxed = [ for a in args -> if a.Expr.Type = typeof<obj> then a.Expr else Expr.Coerce(a.Expr, typeof<obj>
 228202            let call = Expr.Call((if isVoid then wideInvokeVoidMethod else wideInvokeMethod),
 76203                                 [ Expr.Value(siteValue, typeof<CallSite>); Expr.Value(siteDelegate, typeof<Type>); Expr
 300204            if isVoid || resultType = typeof<obj> then call else Expr.Coerce(call, resultType)
 205
 206    let getMember (context: Type) (name: string) (target: Arg) =
 8659207        callsOnly "reading a member" target
 8655208        siteCall (Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ])) [ target ] typeof<obj>
 209
 210    let setMember (context: Type) (name: string) (target: Arg) (value: Arg) =
 166211        callsOnly "setting a member" target
 162212        let csharp = Binder.SetMember(CSharpBinderFlags.None, name, context, [ argInfo target; argInfo value ]) :?> SetM
 162213        siteCall (MetaObjectAwareBinder(FSharpSetMemberBinder(context, name, csharp))) [ target; value ] typeof<obj>
 214
 215    /// C#'s Convert to `resultType`; to a delegate or F# function type through
 216    /// `FSharpConvertBinder`, so a function value becomes a delegate and back (#202). Every
 217    /// other type keeps C#'s binder as it is.
 218    let private convertBinder (context: Type) (flags: CSharpBinderFlags) (resultType: Type) : CallSiteBinder =
 9187219        let csharp = Binder.Convert(flags, resultType, context)
 27285220        if typeof<Delegate>.IsAssignableFrom resultType || FSharp.Reflection.FSharpType.IsFunction resultType then
 324221            FSharpConvertBinder(csharp :?> ConvertBinder) :> CallSiteBinder
 8863222        else csharp
 223
 224    /// C#'s InvokeMember binder wrapped to apply F# function values (see FSharpInvokeMemberBinder)
 225    /// for positional, non-generic calls of any arity; otherwise C#'s binder as is.
 226    let private smartInvokeMember (context: Type) (name: string) (typeArgs: Type list) (discard: bool) (all: Arg list) :
 3327227        let args = List.tail all
 3327228        let flags = resultFlags discard
 6654229        let typeArgSeq = match typeArgs with [] -> null | ts -> ts :> seq<Type>
 19506230        let csharp = Binder.InvokeMember(flags, name, typeArgSeq, context, [ for a in all -> argInfo a ])
 3327231        let positional = allPositional args
 232        // A static target has no instance for the function-member rules, but the argument rules
 233        // (optional parameters, function/delegate conversions) apply to its static methods.
 3327234        if isStatic (List.head all) then
 332235            (if not positional || not typeArgs.IsEmpty then csharp else FSharpStaticInvokeMemberBinder(context, name, cs
 236        else
 237            // Discarded results too: the site is void-returning and the DLR drops the rule's value.
 18954238            let csharpInvoke = Binder.Invoke(flags, context, [ for a in all -> argInfo a ]) :?> InvokeBinder
 10117239            let inner = if not positional || not typeArgs.IsEmpty then csharp else FSharpInvokeMemberBinder(context, nam
 240            // An instance target may be a meta-object: F# function arguments become delegates for it.
 3231241            MetaObjectAwareBinder(inner :?> DynamicMetaObjectBinder) :> CallSiteBinder
 242
 243    /// `x?Name(args)` whose inferred type is `A -> R`: InvokeMember, applying an F# function value
 244    /// held by the member when C# cannot invoke it.
 245    let invokeMemberOrApply (context: Type) (name: string) (typeArgs: Type list) (discard: bool) (target: Arg) (args: Ar
 3027246        let all = target :: args
 9081247        siteCall (smartInvokeMember context name typeArgs discard all) all (if discard then voidType else typeof<obj>)
 248
 249    /// `Dlr.new'<T>(args)`: C#'s `new T(…)` with the constructor chosen by the arguments'
 250    /// runtime types. The type goes in as argument 0 of the site, flagged as a static type,
 251    /// exactly as the C# compiler emits it.
 252    let invokeConstructor (context: Type) (t: Type) (args: Arg list) =
 96253        let all = staticTarget t :: args
 254        // Result typed `t`, as the C# compiler's own site for `new T(…)` is: the binder types a
 255        // constructor's result as `T`, and an obj-typed site would reject that for a value type.
 540256        let csharp = Binder.InvokeConstructor(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ]) :?> Dynami
 96257        let positional = allPositional args
 192258        let binder = if positional then FSharpInvokeConstructorBinder(context, t, csharp) :> CallSiteBinder else csharp 
 96259        siteCall binder all t
 260
 261    /// `Dlr.call target args` / `Dlr.apply args target`: invoke `target` itself, applying it when
 262    /// it is an F# function.
 263    let invokeOrApply (context: Type) (discard: bool) (target: Arg) (args: Arg list) =
 162264        callsOnly "invoking a value (Dlr.call / Dlr.apply)" target
 158265        let all = target :: args
 1002266        let csharp = Binder.Invoke(resultFlags discard, context, [ for a in all -> argInfo a ])
 158267        let positional = allPositional args
 268        let binder =
 170269            if not positional then csharp
 146270            else FSharpInvokeBinder(csharp :?> InvokeBinder) :> CallSiteBinder
 474271        siteCall (MetaObjectAwareBinder(binder :?> DynamicMetaObjectBinder)) all (if discard then voidType else typeof<o
 272
 273    /// `asFunction`'s factories past five, per (function type, invoke site type).
 274    let private factories = System.Collections.Concurrent.ConcurrentDictionary<struct (Type * Type), Delegate>(TypePairC
 275
 276    /// A value read as an F# function type (see FunctionMember): the argument types come from the
 277    /// function type's domains, curried or tupled; `binderFor` gives the site's binder for those
 278    /// typed argument slots (`unit -> R` gets an empty list), and `shortcut` says to return the
 279    /// target itself when it already is a function of that type (a bare value, not a member read).
 280    /// Any arity, curried or tupled: typed helpers up to five, functions built at run time past it.
 281    let private asFunction (context: Type) (functionType: Type) (original: Arg) (shortcut: bool) (binderFor: Arg list ->
 308282        let target = original
 283        let rec domains (t: Type) =
 874284            if FSharp.Reflection.FSharpType.IsFunction t then
 566285                let d, r = FSharp.Reflection.FSharpType.GetFunctionElements t
 566286                let ds, result = domains r
 566287                d :: ds, result
 308288            else [], t
 308289        let ds, resultType = domains functionType
 290        let tupled, argTypes =
 308291            match ds with
 310292            | [ d ] when d = typeof<unit> -> false, []
 192293            | [ d ] when FSharp.Reflection.FSharpType.IsTuple d -> true, List.ofArray (FSharp.Reflection.FSharpType.GetT
 138294            | ds -> false, ds
 295
 296        // With the shortcut the target is evaluated once into a variable that both the type test
 297        // and the invoker read.
 308298        let targetVar = Var("target", typeof<obj>)
 616299        let target = if shortcut then { target with Expr = Expr.Var targetVar } else target
 2756300        let invokeArgs = [ for t in argTypes -> typedArg (Expr.Value(null, t)) ]
 308301        let all = target :: invokeArgs
 302        // `… -> unit` invokes with the result discarded (a void site), as a statement call does.
 308303        let discard = resultType = typeof<unit>
 924304        let invokeSite = site (binderFor all discard) all (if discard then voidType else typeof<obj>)
 924305        let shape = (if tupled then "Tupled" else "Curried") + string argTypes.Length
 306        let built =
 308307            if argTypes.Length > 5 then
 308                // Beyond the typed helpers, the function as a factory compiled once here (see
 309                // FunctionBuilder): the sites and the target in, the function over them out. The
 310                // sites stay in the quotation, as `CallSite` constants (a per-key site lifts them),
 311                // typed as the base: a wide site's delegate type is emitted, which a quotation
 312                // must not name (see WideSite).
 313                let sites =
 176314                    [ yield invokeSite
 248315                      if not discard then yield site (convertBinder context CSharpBinderFlags.None resultType) [ dynamic
 252316                    |> List.map (function Patterns.Value(v, t) -> v, t | _ -> failwith "unreachable")
 592317                let factoryType = Expression.GetFuncType(Array.ofList ([ for _ in sites -> typeof<CallSite> ] @ [ typeof
 318                // The factory depends on the function type and the sites' types, not on the sites:
 319                // compiled once per shape, which a per-key site's template (built again per key, for
 320                // its sites) then finds here rather than compiling and discarding its own.
 88321                let factory =
 88322                    factories.GetOrAdd(struct (functionType, snd sites.Head), fun _ ->
 252323                        let siteParams = [ for i in 0 .. sites.Length - 1 -> Expression.Parameter(typeof<CallSite>, spri
 36324                        let targetParam = Expression.Parameter(typeof<obj>, "target")
 88325                        // Captured: each site as its own type, then the target.
 228326                        let captured = [ for p, (_, t) in List.zip siteParams sites -> Expression.Convert(p, t) :> Expre
 88327                        let finish (captured: Expression list) (args: Expression list) =
 88328                            let invoke (i: int) (args: Expression list) =
 60329                                Expression.Invoke(Expression.Field(captured.[i], "Target"), captured.[i] :: args) :> Exp
 36330                            let raw = invoke 0 (List.last captured :: args)
 72331                            if discard then FunctionBuilder.unitOf raw else invoke 1 [ raw ]
 180332                        let body = FunctionBuilder.build (if tupled then Some (List.head ds) else None) argTypes (if dis
 124333                        Expression.Lambda(factoryType, body, "dlr-functionFactory", siteParams @ [ targetParam ]).Compil
 88334                Expr.Call(Expr.Value(factory, factoryType), factoryType.GetMethod("Invoke"),
 428335                          [ for v, _ in sites -> Expr.Value(v, typeof<CallSite>) ] @ [ target.Expr ])
 220336            elif discard then
 20337                let helper = typeof<FunctionMember>.GetMethod(shape + "Unit")
 40338                let helper = if argTypes.IsEmpty then helper else helper.MakeGenericMethod(Array.ofList argTypes)
 20339                Expr.Call(helper, [ invokeSite; target.Expr ])
 340            else
 200341                let convertSite = site (convertBinder context CSharpBinderFlags.None resultType) [ dynamicArg (Expr.Valu
 200342                let helper = typeof<FunctionMember>.GetMethod(shape).MakeGenericMethod(Array.ofList (argTypes @ [ result
 200343                Expr.Call(helper, [ invokeSite; convertSite; target.Expr ])
 308344        if shortcut then
 345            // A value that already is a function of this type is that function: no invoker.
 48346            Expr.Let(targetVar, original.Expr,
 48347                     Expr.IfThenElse(Expr.TypeTest(Expr.Var targetVar, functionType), Expr.Coerce(Expr.Var targetVar, fu
 260348        else built
 349
 350    /// The binder invoking member `name` with `all` (the target, then the arguments): with no
 351    /// arguments, a property is read (and applied) or a parameterless method invoked.
 352    let private memberInvoker (context: Type) (name: string) (target: Arg) (all: Arg list) (discard: bool) : CallSiteBin
 422353        let flags = resultFlags discard
 422354        if all.Length = 1 then
 122355            let csharp = Binder.InvokeMember(flags, name, null, context, [ argInfo target ]) :?> InvokeMemberBinder
 122356            let csharpInvoke = Binder.Invoke(flags, context, [ argInfo target ]) :?> InvokeBinder
 122357            FSharpReadOrInvokeBinder(context, name, csharp, csharpInvoke) :> CallSiteBinder
 300358        else smartInvokeMember context name [] discard all
 359
 360    /// `x?Name` read as an F# function type: `unit -> R` reads a property or invokes a
 361    /// parameterless method; otherwise a typed InvokeMember site.
 362    let functionMember (context: Type) (name: string) (functionType: Type) (target: Arg) : Expr =
 264363        callsOnly "reading a member as a function" target
 260364        asFunction context functionType target false (memberInvoker context name target)
 365
 366    /// Whether a member read as delegate type `t` is ours (`delegateMember`), and past fourteen
 367    /// parameters the F# function type its invoker takes: tupled, one step over the tuple (up to
 368    /// fourteen, the invoker takes the delegate's own signature). None when `t` is no concrete
 369    /// delegate type, has a byref parameter or result, or past fourteen returns an F# function
 370    /// (which the function type would read as more parameters) or does not convert: such a read
 371    /// stays C#'s.
 372    let delegateRead (t: Type) : Type option option =
 14629373        if not (typeof<Delegate>.IsAssignableFrom t) || t = typeof<Delegate> || t = typeof<MulticastDelegate> || t.Conta
 170374        let invoke = DelegateMembers.invokeOf t
 1790375        let ps = [ for p in invoke.GetParameters() -> p.ParameterType ]
 1232376        if invoke.ReturnType.IsByRef || ps |> List.exists (fun p -> p.IsByRef) then None
 377        else
 340378            let result = if invoke.ReturnType = voidType then typeof<unit> else invoke.ReturnType
 324379            if ps.Length <= 14 then Some None
 24380            elif FSharp.Reflection.FSharpType.IsFunction result then None
 381            else
 8382                let functionType = FSharp.Reflection.FSharpType.MakeFunctionType(FSharp.Reflection.FSharpType.MakeTupleT
 16383                FunctionConversions.tryConversion functionType t |> Option.map (fun _ -> Some functionType)
 384
 385    /// `x?Name` read as a delegate type (#201): C#'s read of the member, converted, unless the
 386    /// member is a method (`FSharpGetMemberOrMethodBinder` yields `MethodGroup`); then the delegate
 387    /// over a `MemberInvokers` instance holding the sites and the target (`FunctionConversions.over`),
 388    /// or past fourteen parameters `functionMember` at `functionType`, converted.
 389    let delegateMember (context: Type) (name: string) (delegateType: Type) (functionType: Type option) (target: Arg) : E
 170390        callsOnly "reading a member" target
 170391        let targetVar = Var("target", target.Expr.Type)
 170392        let target' = { target with Expr = Expr.Var targetVar }
 170393        let valueVar = Var("value", typeof<obj>)
 170394        let csharp = Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ]) :?> GetMemberBinder
 170395        let read = siteCall (FSharpGetMemberOrMethodBinder(context, name, csharp)) [ target' ] typeof<obj>
 396        let invoke (factory: Func<obj, Delegate>) (instance: Expr) =
 170397            Expr.Coerce(Expr.Call(Expr.Value(factory, typeof<Func<obj, Delegate>>), typeof<Func<obj, Delegate>>.GetMetho
 170398                                  [ Expr.Coerce(instance, typeof<obj>) ]), delegateType)
 399        let invoker =
 170400            match functionType with
 401            | None ->
 402                // The delegate straight over a `MemberInvokers` instance holding the sites: one hop
 403                // from the delegate to the site call.
 78404                let invokeMethod = DelegateMembers.invokeOf delegateType
 662405                let ps = [ for p in invokeMethod.GetParameters() -> p.ParameterType ]
 78406                let discard = invokeMethod.ReturnType = voidType
 448407                let all = target' :: [ for t in ps -> typedArg (Expr.Value(null, t)) ]
 234408                let invokeSite = site (memberInvoker context name target' all discard) all (if discard then voidType els
 409                let sites, typeArgs =
 88410                    if discard then [ invokeSite ], ps
 68411                    else [ invokeSite; site (convertBinder context CSharpBinderFlags.None invokeMethod.ReturnType) [ dyn
 390412                let def = typeof<MemberInvokers.Action0>.DeclaringType.GetNestedType((if discard then "Action" else "Fun
 156413                let invokerType = if typeArgs.IsEmpty then def else def.MakeGenericType(Array.ofList typeArgs)
 78414                invoke (FunctionConversions.over invokerType delegateType) (Expr.NewObject(invokerType.GetConstructors()
 415            | Some functionType ->
 416                // Past fourteen the site is wide: the member read as a function, converted
 417                // (`delegateRead` checked that it converts).
 8418                invoke (FunctionConversions.tryConversion functionType delegateType).Value (functionMember context name 
 170419        let converted = siteCall (convertBinder context CSharpBinderFlags.None delegateType) [ dynamicArg (Expr.Var valu
 170420        Expr.Let(targetVar, target.Expr,
 170421          Expr.Let(valueVar, read,
 170422            Expr.IfThenElse(Expr.Call(typeof<MethodGroup>.GetMethod("Is"), [ Expr.Var valueVar ]), invoker, converted)))
 423
 424    /// `Dlr.call x` read as an F# function type: the target itself as that function — a typed
 425    /// Invoke site (F# function values through FSharpInvokeBinder), or the target as it is when
 426    /// it already is a function of the type.
 427    let functionTarget (context: Type) (functionType: Type) (target: Arg) : Expr =
 48428        callsOnly "Dlr.call" target
 48429        asFunction context functionType target true (fun all discard ->
 48430            let flags = resultFlags discard
 360431            FSharpInvokeBinder(Binder.Invoke(flags, context, [ for a in all -> argInfo a ]) :?> InvokeBinder) :> CallSit
 432
 433    let getIndex (context: Type) (target: Arg) (indexes: Arg list) =
 86434        callsOnly "indexing" target
 82435        let all = target :: indexes
 450436        siteCall (Binder.GetIndex(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ])) all typeof<obj>
 437
 438    let setIndex (context: Type) (target: Arg) (indexes: Arg list) (value: Arg) =
 64439        callsOnly "indexing" target
 64440        let all = target :: indexes @ [ value ]
 441        // C#'s SetIndex binder, our value conversion as its error suggestion; a meta-object target
 442        // with an F# function value gets the delegate.
 408443        let csharp = Binder.SetIndex(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ]) :?> SetIndexBinder
 64444        siteCall (MetaObjectAwareBinder(FSharpSetIndexBinder(context, csharp))) all typeof<obj>
 445
 446    /// C#'s `d.Name += v` / `-=`: an IsEvent site decides at run time between the event
 447    /// accessor (`add_Name`/`remove_Name`, invoked as a special name) and read-modify-write
 448    /// (GetMember, AddAssign/SubtractAssign, SetMember flagged as a compound assignment).
 449    /// `target` and `value` must be variables, since both branches mention them. An F# function
 450    /// value converts on both: to the event's delegate type at the accessor (the invoke-member
 451    /// seam), and to the delegate of its signature for a meta-object's member (a COM event's
 452    /// bound event takes delegates only), through `MetaObjectAwareBinder`.
 453    let compoundAssign (context: Type) (name: string) (subtract: bool) (target: Arg) (value: Arg) : Expr =
 102454        callsOnly "addAssign/subtractAssign" target
 98455        let isEvent =
 98456            siteCall (Binder.IsEvent(CSharpBinderFlags.None, name, context)) [ target ] typeof<bool>
 457        let accessor =
 294458            let accessorName = (if subtract then "remove_" else "add_") + name
 98459            let infos = [ argInfo target; argInfo value ]
 98460            let csharp =
 98461                Binder.InvokeMember(CSharpBinderFlags.InvokeSpecialName ||| CSharpBinderFlags.ResultDiscarded, accessorN
 98462            let csharpInvoke = Binder.Invoke(CSharpBinderFlags.ResultDiscarded, context, infos) :?> InvokeBinder
 98463            siteCall (FSharpInvokeMemberBinder(context, accessorName, csharp :?> InvokeMemberBinder, csharpInvoke)) [ ta
 464        let readModifyWrite =
 98465            let current = siteCall (Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ])) [ targe
 196466            let op = if subtract then ExpressionType.SubtractAssign else ExpressionType.AddAssign
 467            let combined =
 98468                let csharp = Binder.BinaryOperation(CSharpBinderFlags.None, op, context, [ argInfo (dynamicArg current);
 98469                siteCall (MetaObjectAwareBinder(csharp :?> DynamicMetaObjectBinder)) [ dynamicArg current; value ] typeo
 49470            let set =
 49471                Binder.SetMember(CSharpBinderFlags.ValueFromCompoundAssignment, name, context, [ argInfo target; argInfo
 98472            siteCall set [ target; dynamicArg combined ] typeof<obj>
 98473        Expr.IfThenElse(isEvent, Expr.Sequential(accessor, Expr.Value(())), Expr.Sequential(readModifyWrite, Expr.Value(
 474
 475    let binaryOperation (context: Type) (op: ExpressionType) (left: Arg) (right: Arg) =
 238476        let csharp = Binder.BinaryOperation(CSharpBinderFlags.None, op, context, [ argInfo left; argInfo right ]) :?> Bi
 476477        let binder = if FSharpBinaryOperationBinder.IsComparison op then FSharpBinaryOperationBinder csharp :> CallSiteB
 238478        siteCall binder [ left; right ] typeof<obj>
 479
 480    let unaryOperation (context: Type) (op: ExpressionType) (operand: Arg) =
 10481        let binder = Binder.UnaryOperation(CSharpBinderFlags.None, op, context, [ argInfo operand ])
 20482        siteCall binder [ operand ] typeof<obj>
 483
 484    /// Implicit conversion of an `obj`-typed expression to `resultType`. `obj` is a no-op and
 485    /// `unit` discards the value.
 486    let convert (context: Type) (resultType: Type) (e: Expr) : Expr =
 9395487        if resultType = typeof<obj> then e
 9025488        elif resultType = typeof<unit> then Expr.Sequential(e, Expr.Value(()))
 8573489        else siteCall (convertBinder context CSharpBinderFlags.None resultType) [ dynamicArg e ] resultType
 490
 491    /// Explicit conversion (a C# cast) of an `obj`-typed expression to `resultType`.
 492    let convertExplicit (context: Type) (resultType: Type) (e: Expr) : Expr =
 26493        siteCall (convertBinder context CSharpBinderFlags.ConvertExplicit resultType) [ dynamicArg e ] resultType

Methods/Properties

dynamicArg(Microsoft.FSharp.Quotations.FSharpExpr)
staticTarget(System.Type)
isStatic(FSharp.Interop.Dlr.Binders/Arg)
callsOnly(System.String,FSharp.Interop.Dlr.Binders/Arg)
typedArg(Microsoft.FSharp.Quotations.FSharpExpr)
withFlag(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,FSharp.Interop.Dlr.Binders/Arg)
constant(FSharp.Interop.Dlr.Binders/Arg)
byRefArg(System.Boolean,System.Type,Microsoft.FSharp.Quotations.FSharpExpr,System.Object)
named(System.String,FSharp.Interop.Dlr.Binders/Arg)
argInfo(FSharp.Interop.Dlr.Binders/Arg)
resultFlags(System.Boolean)
allPositional(Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
site(System.Runtime.CompilerServices.CallSiteBinder,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,System.Type)
wideMethod(System.String)
byRefHolderType(Microsoft.FSharp.Collections.FSharpList`1<System.Type>)
byRefHolderPath(System.Type,System.Int32)
byRefHolderOf(Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
byRefSite(System.Runtime.CompilerServices.CallSiteBinder,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,System.Type)
invokeMemberByRef(System.Type,System.String,Microsoft.FSharp.Collections.FSharpList`1<System.Type>,System.Boolean,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
invokeByRef(System.Type,System.Boolean,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
invokeConstructorByRef(System.Type,System.Type,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
siteCall(System.Runtime.CompilerServices.CallSiteBinder,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,System.Type)
getMember(System.Type,System.String,FSharp.Interop.Dlr.Binders/Arg)
setMember(System.Type,System.String,FSharp.Interop.Dlr.Binders/Arg,FSharp.Interop.Dlr.Binders/Arg)
convertBinder(System.Type,Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Type)
smartInvokeMember(System.Type,System.String,Microsoft.FSharp.Collections.FSharpList`1<System.Type>,System.Boolean,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
invokeMemberOrApply(System.Type,System.String,Microsoft.FSharp.Collections.FSharpList`1<System.Type>,System.Boolean,FSharp.Interop.Dlr.Binders/Arg,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
invokeConstructor(System.Type,System.Type,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
invokeOrApply(System.Type,System.Boolean,FSharp.Interop.Dlr.Binders/Arg,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
asFunction(System.Type,System.Type,FSharp.Interop.Dlr.Binders/Arg,System.Boolean,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,Microsoft.FSharp.Core.FSharpFunc`2<System.Boolean,System.Runtime.CompilerServices.CallSiteBinder>>)
domains@283(System.Type)
Invoke(System.Type)
memberInvoker(System.Type,System.String,FSharp.Interop.Dlr.Binders/Arg,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,System.Boolean)
functionMember(System.Type,System.String,System.Type,FSharp.Interop.Dlr.Binders/Arg)
delegateRead(System.Type)
delegateMember(System.Type,System.String,System.Type,Microsoft.FSharp.Core.FSharpOption`1<System.Type>,FSharp.Interop.Dlr.Binders/Arg)
invoke@396-1(System.Type,System.Func`2<System.Object,System.Delegate>,Microsoft.FSharp.Quotations.FSharpExpr)
Invoke(System.Func`2<System.Object,System.Delegate>,Microsoft.FSharp.Quotations.FSharpExpr)
functionTarget(System.Type,System.Type,FSharp.Interop.Dlr.Binders/Arg)
getIndex(System.Type,FSharp.Interop.Dlr.Binders/Arg,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>)
setIndex(System.Type,FSharp.Interop.Dlr.Binders/Arg,Microsoft.FSharp.Collections.FSharpList`1<FSharp.Interop.Dlr.Binders/Arg>,FSharp.Interop.Dlr.Binders/Arg)
compoundAssign(System.Type,System.String,System.Boolean,FSharp.Interop.Dlr.Binders/Arg,FSharp.Interop.Dlr.Binders/Arg)
binaryOperation(System.Type,System.Linq.Expressions.ExpressionType,FSharp.Interop.Dlr.Binders/Arg,FSharp.Interop.Dlr.Binders/Arg)
unaryOperation(System.Type,System.Linq.Expressions.ExpressionType,FSharp.Interop.Dlr.Binders/Arg)
unaryOperation(System.Type,System.Linq.Expressions.ExpressionType,FSharp.Interop.Dlr.Binders/Arg)
convert(System.Type,System.Type,Microsoft.FSharp.Quotations.FSharpExpr)
convertExplicit(System.Type,System.Type,Microsoft.FSharp.Quotations.FSharpExpr)