< 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%
Invoke(...)100%22100%
domains@283(...)100%22100%
memberInvoker(...)100%22100%
functionMember(...)100%11100%
delegateRead(...)90%2020100%
delegateMember(...)100%22100%
Invoke(...)100%11100%
invoke@396-1(...)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) =
 1717433        { 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) =
 33038        { Expr = Expr.Value(t, typeof<Type>); Type = typeof<Type>; Flags = CSharpArgumentInfoFlags.UseCompileTimeType ||
 39
 1018040    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) =
 1500945        if isStatic target then
 3646            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) =
 1130752        if e.Type = typeof<obj> then dynamicArg e
 910353        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.
 920458        let current: int = LanguagePrimitives.EnumToValue arg.Flags
 920459        let added: int = LanguagePrimitives.EnumToValue flag
 920460        { 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).
 503464    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) =
 655869        withFlag (if isOut then CSharpArgumentInfoFlags.IsOut else CSharpArgumentInfoFlags.IsRef)
 218670            { Expr = initial; Type = t.MakeByRefType(); Flags = CSharpArgumentInfoFlags.UseCompileTimeType; Name = null;
 71
 72    let named (name: string) (arg: Arg) =
 396873        { withFlag CSharpArgumentInfoFlags.NamedArgument arg with Name = name }
 74
 2714875    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.
 822878    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).
 1887381    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) =
 3540785        let delegateType =
 15377086            Expression.GetDelegateType(Array.ofList (typeof<CallSite> :: [ for a in args -> a.Type ] @ [ resultType ]))
 3540787        let siteType = typedefof<CallSite<_>>.MakeGenericType delegateType
 3540788        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
 6114    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.
 983121    let byRefHolderType (types: Type list) : Type = Tuples.valueTupleOf types
 122    /// Element `i` of a holder: `Item(i+1)`, through `Rest` past the seventh.
 2122123    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.
 26504151    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 =
 11826156        let delegateType = Expression.GetDelegateType(Array.ofList (typeof<CallSite> :: [ for a in all -> a.Type ] @ [ r
 1934157        let site = typedefof<CallSite<_>>.MakeGenericType(delegateType).GetMethod("Create").Invoke(null, [| box binder |
 26090158        let byRefs = [| for i, a in List.indexed all do if a.Type.IsByRef then yield i |]
 25988159        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.
 1934161        let sameAs =
 9892162            [| for i, a in List.indexed all ->
 11946163                if isNull a.Alias then -1
 2447164                else match all |> List.take i |> List.tryFindIndex (fun b -> obj.ReferenceEquals(b.Alias, a.Alias)) with
 1934165        let holder = byRefHolderOf all
 23874166        let boxed = [ for a in all -> if a.Expr.Type = typeof<obj> then a.Expr else Expr.Coerce(a.Expr, typeof<obj>) ]
 1934167        Expr.Call(byRefInvokeMethod.MakeGenericMethod holder,
 1934168                  [ Expr.Value(site, typeof<CallSite>); Expr.Value(delegateType, typeof<Type>)
 1934169                    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 =
 1820173        let flags = resultFlags discard
 3640174        let typeArgSeq = match typeArgs with [] -> null | ts -> ts :> seq<Type>
 14758175        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 =
 102179        callsOnly "invoking a value (Dlr.call / Dlr.apply)" (List.head all)
 102180        let flags = resultFlags discard
 1500181        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 =
 12185        let all = staticTarget t :: args
 72186        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 =
 34085191        let siteExpr = site binder args resultType
 68170192        let siteValue = match siteExpr with FSharp.Quotations.Patterns.Value(v, _) -> v | _ -> null
 34085193        let siteType = siteExpr.Type
 34085194        let siteDelegate = siteType.GetGenericArguments().[0]
 195        // `args` holds the target too; the site's delegate takes the CallSite before them.
 34085196        if DelegateMembers.funcFits (args.Length + 1) then
 33971197            let target = Expr.FieldGet(siteExpr, siteType.GetField("Target"))
 143992198            Expr.Call(target, siteDelegate.GetMethod("Invoke"), siteExpr :: [ for a in args -> a.Expr ])
 199        else
 114200            let isVoid = resultType = typeof<Action>.GetMethod("Invoke").ReturnType
 6750201            let boxed = [ for a in args -> if a.Expr.Type = typeof<obj> then a.Expr else Expr.Coerce(a.Expr, typeof<obj>
 342202            let call = Expr.Call((if isVoid then wideInvokeVoidMethod else wideInvokeMethod),
 114203                                 [ Expr.Value(siteValue, typeof<CallSite>); Expr.Value(siteDelegate, typeof<Type>); Expr
 450204            if isVoid || resultType = typeof<obj> then call else Expr.Coerce(call, resultType)
 205
 206    let getMember (context: Type) (name: string) (target: Arg) =
 13291207        callsOnly "reading a member" target
 13285208        siteCall (Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ])) [ target ] typeof<obj>
 209
 210    let setMember (context: Type) (name: string) (target: Arg) (value: Arg) =
 254211        callsOnly "setting a member" target
 248212        let csharp = Binder.SetMember(CSharpBinderFlags.None, name, context, [ argInfo target; argInfo value ]) :?> SetM
 248213        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 =
 14353219        let csharp = Binder.Convert(flags, resultType, context)
 42649220        if typeof<Delegate>.IsAssignableFrom resultType || FSharp.Reflection.FSharpType.IsFunction resultType then
 482221            FSharpConvertBinder(csharp :?> ConvertBinder) :> CallSiteBinder
 13871222        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) :
 5279227        let args = List.tail all
 5279228        let flags = resultFlags discard
 10558229        let typeArgSeq = match typeArgs with [] -> null | ts -> ts :> seq<Type>
 30810230        let csharp = Binder.InvokeMember(flags, name, typeArgSeq, context, [ for a in all -> argInfo a ])
 5279231        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.
 5279234        if isStatic (List.head all) then
 498235            (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.
 29982238            let csharpInvoke = Binder.Invoke(flags, context, [ for a in all -> argInfo a ]) :?> InvokeBinder
 16214239            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.
 5135241            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
 4833246        let all = target :: args
 14499247        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) =
 144253        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.
 810256        let csharp = Binder.InvokeConstructor(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ]) :?> Dynami
 144257        let positional = allPositional args
 288258        let binder = if positional then FSharpInvokeConstructorBinder(context, t, csharp) :> CallSiteBinder else csharp 
 144259        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) =
 252264        callsOnly "invoking a value (Dlr.call / Dlr.apply)" target
 246265        let all = target :: args
 1546266        let csharp = Binder.Invoke(resultFlags discard, context, [ for a in all -> argInfo a ])
 246267        let positional = allPositional args
 268        let binder =
 264269            if not positional then csharp
 228270            else FSharpInvokeBinder(csharp :?> InvokeBinder) :> CallSiteBinder
 738271        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 ->
 460282        let target = original
 283        let rec domains (t: Type) =
 1302284            if FSharp.Reflection.FSharpType.IsFunction t then
 842285                let d, r = FSharp.Reflection.FSharpType.GetFunctionElements t
 842286                let ds, result = domains r
 842287                d :: ds, result
 460288            else [], t
 460289        let ds, resultType = domains functionType
 290        let tupled, argTypes =
 460291            match ds with
 464292            | [ d ] when d = typeof<unit> -> false, []
 286293            | [ d ] when FSharp.Reflection.FSharpType.IsTuple d -> true, List.ofArray (FSharp.Reflection.FSharpType.GetT
 206294            | 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.
 460298        let targetVar = Var("target", typeof<obj>)
 920299        let target = if shortcut then { target with Expr = Expr.Var targetVar } else target
 4104300        let invokeArgs = [ for t in argTypes -> typedArg (Expr.Value(null, t)) ]
 460301        let all = target :: invokeArgs
 302        // `… -> unit` invokes with the result discarded (a void site), as a statement call does.
 460303        let discard = resultType = typeof<unit>
 1380304        let invokeSite = site (binderFor all discard) all (if discard then voidType else typeof<obj>)
 1380305        let shape = (if tupled then "Tupled" else "Curried") + string argTypes.Length
 306        let built =
 460307            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 =
 260314                    [ yield invokeSite
 368315                      if not discard then yield site (convertBinder context CSharpBinderFlags.None resultType) [ dynamic
 372316                    |> List.map (function Patterns.Value(v, t) -> v, t | _ -> failwith "unreachable")
 874317                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.
 130321                let factory =
 130322                    factories.GetOrAdd(struct (functionType, snd sites.Head), fun _ ->
 378323                        let siteParams = [ for i in 0 .. sites.Length - 1 -> Expression.Parameter(typeof<CallSite>, spri
 54324                        let targetParam = Expression.Parameter(typeof<obj>, "target")
 130325                        // Captured: each site as its own type, then the target.
 342326                        let captured = [ for p, (_, t) in List.zip siteParams sites -> Expression.Convert(p, t) :> Expre
 130327                        let finish (captured: Expression list) (args: Expression list) =
 130328                            let invoke (i: int) (args: Expression list) =
 90329                                Expression.Invoke(Expression.Field(captured.[i], "Target"), captured.[i] :: args) :> Exp
 54330                            let raw = invoke 0 (List.last captured :: args)
 108331                            if discard then FunctionBuilder.unitOf raw else invoke 1 [ raw ]
 270332                        let body = FunctionBuilder.build (if tupled then Some (List.head ds) else None) argTypes (if dis
 184333                        Expression.Lambda(factoryType, body, "dlr-functionFactory", siteParams @ [ targetParam ]).Compil
 130334                Expr.Call(Expr.Value(factory, factoryType), factoryType.GetMethod("Invoke"),
 632335                          [ for v, _ in sites -> Expr.Value(v, typeof<CallSite>) ] @ [ target.Expr ])
 330336            elif discard then
 30337                let helper = typeof<FunctionMember>.GetMethod(shape + "Unit")
 60338                let helper = if argTypes.IsEmpty then helper else helper.MakeGenericMethod(Array.ofList argTypes)
 30339                Expr.Call(helper, [ invokeSite; target.Expr ])
 340            else
 300341                let convertSite = site (convertBinder context CSharpBinderFlags.None resultType) [ dynamicArg (Expr.Valu
 300342                let helper = typeof<FunctionMember>.GetMethod(shape).MakeGenericMethod(Array.ofList (argTypes @ [ result
 300343                Expr.Call(helper, [ invokeSite; convertSite; target.Expr ])
 460344        if shortcut then
 345            // A value that already is a function of this type is that function: no invoker.
 72346            Expr.Let(targetVar, original.Expr,
 72347                     Expr.IfThenElse(Expr.TypeTest(Expr.Var targetVar, functionType), Expr.Coerce(Expr.Var targetVar, fu
 388348        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
 628353        let flags = resultFlags discard
 628354        if all.Length = 1 then
 182355            let csharp = Binder.InvokeMember(flags, name, null, context, [ argInfo target ]) :?> InvokeMemberBinder
 182356            let csharpInvoke = Binder.Invoke(flags, context, [ argInfo target ]) :?> InvokeBinder
 182357            FSharpReadOrInvokeBinder(context, name, csharp, csharpInvoke) :> CallSiteBinder
 446358        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 =
 394363        callsOnly "reading a member as a function" target
 388364        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 =
 22679373        if not (typeof<Delegate>.IsAssignableFrom t) || t = typeof<Delegate> || t = typeof<MulticastDelegate> || t.Conta
 252374        let invoke = DelegateMembers.invokeOf t
 2668375        let ps = [ for p in invoke.GetParameters() -> p.ParameterType ]
 1835376        if invoke.ReturnType.IsByRef || ps |> List.exists (fun p -> p.IsByRef) then None
 377        else
 504378            let result = if invoke.ReturnType = voidType then typeof<unit> else invoke.ReturnType
 480379            if ps.Length <= 14 then Some None
 36380            elif FSharp.Reflection.FSharpType.IsFunction result then None
 381            else
 12382                let functionType = FSharp.Reflection.FSharpType.MakeFunctionType(FSharp.Reflection.FSharpType.MakeTupleT
 24383                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
 252390        callsOnly "reading a member" target
 252391        let targetVar = Var("target", target.Expr.Type)
 252392        let target' = { target with Expr = Expr.Var targetVar }
 252393        let valueVar = Var("value", typeof<obj>)
 252394        let csharp = Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ]) :?> GetMemberBinder
 252395        let read = siteCall (FSharpGetMemberOrMethodBinder(context, name, csharp)) [ target' ] typeof<obj>
 396        let invoke (factory: Func<obj, Delegate>) (instance: Expr) =
 252397            Expr.Coerce(Expr.Call(Expr.Value(factory, typeof<Func<obj, Delegate>>), typeof<Func<obj, Delegate>>.GetMetho
 252398                                  [ Expr.Coerce(instance, typeof<obj>) ]), delegateType)
 399        let invoker =
 252400            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.
 117404                let invokeMethod = DelegateMembers.invokeOf delegateType
 993405                let ps = [ for p in invokeMethod.GetParameters() -> p.ParameterType ]
 117406                let discard = invokeMethod.ReturnType = voidType
 672407                let all = target' :: [ for t in ps -> typedArg (Expr.Value(null, t)) ]
 351408                let invokeSite = site (memberInvoker context name target' all discard) all (if discard then voidType els
 409                let sites, typeArgs =
 132410                    if discard then [ invokeSite ], ps
 102411                    else [ invokeSite; site (convertBinder context CSharpBinderFlags.None invokeMethod.ReturnType) [ dyn
 585412                let def = typeof<MemberInvokers.Action0>.DeclaringType.GetNestedType((if discard then "Action" else "Fun
 234413                let invokerType = if typeArgs.IsEmpty then def else def.MakeGenericType(Array.ofList typeArgs)
 117414                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).
 12418                invoke (FunctionConversions.tryConversion functionType delegateType).Value (functionMember context name 
 252419        let converted = siteCall (convertBinder context CSharpBinderFlags.None delegateType) [ dynamicArg (Expr.Var valu
 252420        Expr.Let(targetVar, target.Expr,
 252421          Expr.Let(valueVar, read,
 252422            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 =
 72428        callsOnly "Dlr.call" target
 72429        asFunction context functionType target true (fun all discard ->
 72430            let flags = resultFlags discard
 540431            FSharpInvokeBinder(Binder.Invoke(flags, context, [ for a in all -> argInfo a ]) :?> InvokeBinder) :> CallSit
 432
 433    let getIndex (context: Type) (target: Arg) (indexes: Arg list) =
 142434        callsOnly "indexing" target
 136435        let all = target :: indexes
 740436        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) =
 100439        callsOnly "indexing" target
 100440        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.
 636443        let csharp = Binder.SetIndex(CSharpBinderFlags.None, context, [ for a in all -> argInfo a ]) :?> SetIndexBinder
 100444        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 =
 150454        callsOnly "addAssign/subtractAssign" target
 144455        let isEvent =
 144456            siteCall (Binder.IsEvent(CSharpBinderFlags.None, name, context)) [ target ] typeof<bool>
 457        let accessor =
 432458            let accessorName = (if subtract then "remove_" else "add_") + name
 144459            let infos = [ argInfo target; argInfo value ]
 144460            let csharp =
 144461                Binder.InvokeMember(CSharpBinderFlags.InvokeSpecialName ||| CSharpBinderFlags.ResultDiscarded, accessorN
 144462            let csharpInvoke = Binder.Invoke(CSharpBinderFlags.ResultDiscarded, context, infos) :?> InvokeBinder
 144463            siteCall (FSharpInvokeMemberBinder(context, accessorName, csharp :?> InvokeMemberBinder, csharpInvoke)) [ ta
 464        let readModifyWrite =
 144465            let current = siteCall (Binder.GetMember(CSharpBinderFlags.None, name, context, [ argInfo target ])) [ targe
 288466            let op = if subtract then ExpressionType.SubtractAssign else ExpressionType.AddAssign
 467            let combined =
 144468                let csharp = Binder.BinaryOperation(CSharpBinderFlags.None, op, context, [ argInfo (dynamicArg current);
 144469                siteCall (MetaObjectAwareBinder(csharp :?> DynamicMetaObjectBinder)) [ dynamicArg current; value ] typeo
 72470            let set =
 72471                Binder.SetMember(CSharpBinderFlags.ValueFromCompoundAssignment, name, context, [ argInfo target; argInfo
 144472            siteCall set [ target; dynamicArg combined ] typeof<obj>
 144473        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) =
 360476        let csharp = Binder.BinaryOperation(CSharpBinderFlags.None, op, context, [ argInfo left; argInfo right ]) :?> Bi
 720477        let binder = if FSharpBinaryOperationBinder.IsComparison op then FSharpBinaryOperationBinder csharp :> CallSiteB
 360478        siteCall binder [ left; right ] typeof<obj>
 479
 480    let unaryOperation (context: Type) (op: ExpressionType) (operand: Arg) =
 15481        let binder = Binder.UnaryOperation(CSharpBinderFlags.None, op, context, [ argInfo operand ])
 30482        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 =
 14759487        if resultType = typeof<obj> then e
 14135488        elif resultType = typeof<unit> then Expr.Sequential(e, Expr.Value(()))
 13439489        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 =
 40493        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>>)
Invoke(System.Type)
domains@283(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(System.Func`2<System.Object,System.Delegate>,Microsoft.FSharp.Quotations.FSharpExpr)
invoke@396-1(System.Type,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)