| | | 1 | | namespace FSharp.Interop.Dlr |
| | | 2 | | |
| | | 3 | | open System |
| | | 4 | | open System.Diagnostics.CodeAnalysis |
| | | 5 | | open System.Dynamic |
| | | 6 | | open System.Linq.Expressions |
| | | 7 | | open System.Reflection |
| | | 8 | | open System.Runtime.CompilerServices |
| | | 9 | | open Microsoft.CSharp.RuntimeBinder |
| | | 10 | | open FSharp.Quotations |
| | | 11 | | |
| | | 12 | | /// The accessibility rule the C# binder applies from its context type — public; internal from the |
| | | 13 | | /// assembly or one it names in [<InternalsVisibleTo>] (F# `private` compiles to internal); |
| | | 14 | | /// protected from a derived type, through a receiver of that type; private from inside the |
| | | 15 | | /// declaring type — for a member and for its declaring type at every nesting level (a constructed |
| | | 16 | | /// generic is as visible as its arguments). Our own reflection lookups apply the same rule. |
| | | 17 | | module internal Accessibility = |
| | | 18 | | /// Whether `declaring`'s assembly opens its internals to `context`'s: the same one, or one |
| | | 19 | | /// it names in an [<InternalsVisibleTo>] (the C# binder honours that too). Per pair, cached. |
| | | 20 | | let private opensTo = System.Collections.Concurrent.ConcurrentDictionary<struct (Reflection.Assembly * Reflection.As |
| | | 21 | | let private sameAssembly (context: Type) (declaring: Type) = |
| | | 22 | | let c, d = context.Assembly, declaring.Assembly |
| | | 23 | | c = d |
| | | 24 | | || opensTo.GetOrAdd(struct (d, c), fun _ -> |
| | | 25 | | let name = c.GetName().Name |
| | | 26 | | d.GetCustomAttributes(typeof<System.Runtime.CompilerServices.InternalsVisibleToAttribute>, false) |
| | | 27 | | |> Seq.cast<System.Runtime.CompilerServices.InternalsVisibleToAttribute> |
| | | 28 | | |> Seq.exists (fun a -> let n = a.AssemblyName in (match n.IndexOf ',' with -1 -> n | i -> n.Substring(0, i) |
| | | 29 | | /// A generic type by its definition: a type nested in `Outer<T>` has the open `Outer<T>` as |
| | | 30 | | /// its DeclaringType, which no constructed `Outer<int>` is assignable to. |
| | | 31 | | let private definition (t: Type) = if t.IsGenericType && not t.IsGenericTypeDefinition then t.GetGenericTypeDefiniti |
| | | 32 | | let rec private within (context: Type) (declaring: Type) = |
| | | 33 | | not (isNull context) && (definition context = definition declaring || (context.IsNested && within context.Declar |
| | | 34 | | let rec private derived (context: Type) (declaring: Type) = |
| | | 35 | | let d = definition declaring |
| | | 36 | | let rec bases (t: Type) = not (isNull t) && (definition t = d || bases t.BaseType) |
| | | 37 | | not (isNull context) && (bases context || (context.IsNested && derived context.DeclaringType declaring)) |
| | | 38 | | |
| | | 39 | | /// C#'s qualifier rule for a protected instance member: the receiver's type is the context |
| | | 40 | | /// (or an enclosing type) or a subclass of it — not the base, nor a sibling derived type. |
| | | 41 | | /// No receiver (a static member, a nested type) has no such rule. |
| | | 42 | | let rec private qualifies (context: Type) (receiver: Type) = |
| | | 43 | | isNull receiver || (not (isNull context) && (context.IsAssignableFrom receiver || (context.IsNested && qualifies |
| | | 44 | | |
| | | 45 | | /// The C# rule: public; internal from the assembly; protected from a derived type, through |
| | | 46 | | /// a receiver of that type; protected internal from either; private protected from a |
| | | 47 | | /// derived type in the assembly; private from inside the declaring type (including nested |
| | | 48 | | /// types). |
| | | 49 | | let private accessible (context: Type) (declaring: Type) (receiver: Type) (isPublic, isAssembly, isFamily, isFamilyO |
| | | 50 | | let family () = derived context declaring && qualifies context receiver |
| | | 51 | | isPublic |
| | | 52 | | || (isAssembly && sameAssembly context declaring) |
| | | 53 | | || (isFamily && family ()) |
| | | 54 | | || (isFamilyOrAssembly && (sameAssembly context declaring || family ())) |
| | | 55 | | || (isFamilyAndAssembly && sameAssembly context declaring && family ()) |
| | | 56 | | || (isPrivate && within context declaring) |
| | | 57 | | |
| | | 58 | | /// Whether the type itself can be named from `context`, by the same rule at each nesting |
| | | 59 | | /// level: C# `dynamic` binds against an inaccessible runtime type (an `internal` class of |
| | | 60 | | /// another assembly, an anonymous type) as its nearest accessible base, so a public member |
| | | 61 | | /// of such a type is not found either. |
| | | 62 | | let rec private typeVisible (context: Type) (t: Type) = |
| | | 63 | | if isNull t then true |
| | | 64 | | // A constructed generic is as visible as its arguments (`List<Hidden>` is not). |
| | | 65 | | elif t.IsConstructedGenericType then |
| | | 66 | | typeVisible context (t.GetGenericTypeDefinition()) && (t.GetGenericArguments() |> Array.forall (typeVisible |
| | | 67 | | elif t.IsNested then |
| | | 68 | | accessible context t.DeclaringType null (t.IsNestedPublic, t.IsNestedAssembly, t.IsNestedFamily, t.IsNestedF |
| | | 69 | | && typeVisible context t.DeclaringType |
| | | 70 | | else t.IsPublic || sameAssembly context t |
| | | 71 | | |
| | | 72 | | /// `receiver` is the target's runtime type for an instance member (the qualifier rule). |
| | | 73 | | let method' (context: Type) (receiver: Type) (m: MethodBase) = |
| | | 74 | | typeVisible context m.DeclaringType |
| | | 75 | | && accessible context m.DeclaringType (if m.IsStatic then null else receiver) (m.IsPublic, m.IsAssembly, m.IsFam |
| | | 76 | | |
| | | 77 | | let field (context: Type) (receiver: Type) (f: FieldInfo) = |
| | | 78 | | typeVisible context f.DeclaringType |
| | | 79 | | && accessible context f.DeclaringType (if f.IsStatic then null else receiver) (f.IsPublic, f.IsAssembly, f.IsFam |
| | | 80 | | |
| | | 81 | | let all = BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.Instance |
| | | 82 | | |
| | | 83 | | /// The implicit conversions C# applies to an argument, so our own rules accept what its binder would. |
| | | 84 | | module internal Conversions = |
| | | 85 | | /// C#'s implicit numeric conversions, so an `int` argument fits an `int64` or `float` domain. |
| | | 86 | | let widens (from: Type) (``to``: Type) = |
| | | 87 | | let n = [ typeof<sbyte>, [ typeof<int16>; typeof<int32>; typeof<int64>; typeof<single>; typeof<double>; typeof<d |
| | | 88 | | typeof<byte>, [ typeof<int16>; typeof<uint16>; typeof<int32>; typeof<uint32>; typeof<int64>; typeof<ui |
| | | 89 | | typeof<int16>, [ typeof<int32>; typeof<int64>; typeof<single>; typeof<double>; typeof<decimal> ] |
| | | 90 | | typeof<uint16>, [ typeof<int32>; typeof<uint32>; typeof<int64>; typeof<uint64>; typeof<single>; typeof |
| | | 91 | | typeof<int32>, [ typeof<int64>; typeof<single>; typeof<double>; typeof<decimal> ] |
| | | 92 | | typeof<uint32>, [ typeof<int64>; typeof<uint64>; typeof<single>; typeof<double>; typeof<decimal> ] |
| | | 93 | | typeof<int64>, [ typeof<single>; typeof<double>; typeof<decimal> ] |
| | | 94 | | typeof<uint64>, [ typeof<single>; typeof<double>; typeof<decimal> ] |
| | | 95 | | typeof<char>, [ typeof<uint16>; typeof<int32>; typeof<uint32>; typeof<int64>; typeof<uint64>; typeof<s |
| | | 96 | | typeof<single>, [ typeof<double> ] ] |
| | | 97 | | n |> List.exists (fun (f, ts) -> f = from && List.contains ``to`` ts) |
| | | 98 | | |
| | | 99 | | /// Assignable, or a C# implicit numeric widening (`Expression.Convert` does the widening in the rule). |
| | | 100 | | let fits (paramType: Type) (argType: Type) = paramType.IsAssignableFrom argType || widens argType paramType |
| | | 101 | | |
| | | 102 | | /// Two generic helpers over quotations, each the one place its idiom is spelled out. |
| | | 103 | | module internal Quotation = |
| | | 104 | | /// The method the first call in `e` calls: how a MethodInfo is taken from a quotation of a |
| | | 105 | | /// call (`<@ f x @>`; a curried static member quotes as an application of a lambda around it). |
| | | 106 | | let methodOf (e: Expr) : MethodInfo = |
| | | 107 | | let rec find (e: Expr) = |
| | | 108 | | match e with |
| | | 109 | | | Patterns.Call(_, mi, _) -> Some mi |
| | | 110 | | | ExprShape.ShapeVar _ -> None |
| | | 111 | | | ExprShape.ShapeLambda(_, body) -> find body |
| | | 112 | | | ExprShape.ShapeCombination(_, args) -> List.tryPick find args |
| | | 113 | | match find e with |
| | | 114 | | | Some mi -> mi |
| | | 115 | | | None -> invalidArg "e" (sprintf "a quotation of a call, not %A" e) |
| | | 116 | | |
| | | 117 | | /// One level of a rewrite: `f` on each child of `e`, the node rebuilt around the results. The |
| | | 118 | | /// fallthrough of every walk that rewrites the nodes it knows and recurses into the rest. |
| | | 119 | | let rebuild (f: Expr -> Expr) (e: Expr) : Expr = |
| | | 120 | | match e with |
| | | 121 | | | ExprShape.ShapeVar _ -> e |
| | | 122 | | | ExprShape.ShapeLambda(v, body) -> Expr.Lambda(v, f body) |
| | | 123 | | | ExprShape.ShapeCombination(shape, args) -> ExprShape.RebuildShapeCombination(shape, List.map f args) |
| | | 124 | | |
| | | 125 | | /// CLR tuples past seven elements, `Tuple` and `ValueTuple` alike: the first seven, then the rest |
| | | 126 | | /// as an eighth, `Rest`, itself a tuple. The one place that nesting is spelled out — for building |
| | | 127 | | /// a tuple's type, reading an element, constructing one in a LINQ tree or a quotation, or at run time. |
| | | 128 | | module internal Tuples = |
| | | 129 | | let private valueTupleDefinitions = |
| | | 130 | | [| typedefof<ValueTuple<_>>; typedefof<ValueTuple<_, _>>; typedefof<ValueTuple<_, _, _>>; typedefof<ValueTuple<_ |
| | | 131 | | typedefof<ValueTuple<_, _, _, _, _>>; typedefof<ValueTuple<_, _, _, _, _, _>>; typedefof<ValueTuple<_, _, _, |
| | | 132 | | typedefof<ValueTuple<_, _, _, _, _, _, _, _>> |] |
| | | 133 | | |
| | | 134 | | /// The `ValueTuple` of these element types (one or more). |
| | | 135 | | let rec valueTupleOf (types: Type list) : Type = |
| | | 136 | | if types.Length <= 7 then valueTupleDefinitions.[types.Length - 1].MakeGenericType(Array.ofList types) |
| | | 137 | | else valueTupleDefinitions.[7].MakeGenericType(Array.ofList (List.take 7 types @ [ valueTupleOf (List.skip 7 typ |
| | | 138 | | |
| | | 139 | | /// The member names leading to element `i`: `Item(i+1)`, after a `Rest` per seven before it. |
| | | 140 | | let rec elementPath (i: int) : string list = |
| | | 141 | | if i < 7 then [ sprintf "Item%d" (i + 1) ] else "Rest" :: elementPath (i - 7) |
| | | 142 | | |
| | | 143 | | /// Element `i` of a tuple in a LINQ tree (a reference tuple's property, a struct one's field). |
| | | 144 | | let element (tuple: Expression) (i: int) : Expression = |
| | | 145 | | elementPath i |> List.fold (fun e name -> Expression.PropertyOrField(e, name) :> Expression) tuple |
| | | 146 | | |
| | | 147 | | /// The fields leading to element `i` of a `ValueTuple` (for a quotation's FieldGet). |
| | | 148 | | let fieldPath (valueTuple: Type) (i: int) : FieldInfo list = |
| | | 149 | | elementPath i |> List.mapFold (fun (t: Type) name -> let f = t.GetField name in f, f.FieldType) valueTuple |> fs |
| | | 150 | | |
| | | 151 | | /// The arguments of a tuple constructor: the first seven values and the rest built by `nest`. |
| | | 152 | | let private split (t: Type) (values: 'E list) (nest: Type -> 'E list -> 'E) = |
| | | 153 | | let elements = t.GetGenericArguments() |
| | | 154 | | if elements.Length = 8 then elements, List.take 7 values @ [ nest elements.[7] (List.skip 7 values) ] else eleme |
| | | 155 | | |
| | | 156 | | /// A tuple of type `t` (reference or struct) from these values, in a LINQ tree. |
| | | 157 | | let rec newExpression (t: Type) (values: Expression list) : Expression = |
| | | 158 | | let elements, args = split t values newExpression |
| | | 159 | | Expression.New(t.GetConstructor elements, args) :> Expression |
| | | 160 | | |
| | | 161 | | /// A tuple of type `t` (reference or struct) from these values, in a quotation. |
| | | 162 | | let rec newQuotation (t: Type) (values: Expr list) : Expr = |
| | | 163 | | let elements, args = split t values newQuotation |
| | | 164 | | Expr.NewObject(t.GetConstructor elements, args) |
| | | 165 | | |
| | | 166 | | /// A tuple of type `t` from these values, at run time: only `Binders.ByRefSite`'s body, |
| | | 167 | | /// which never runs (see there), so excluded from coverage with it. |
| | | 168 | | [<ExcludeFromCodeCoverage>] |
| | | 169 | | let rec make (t: Type) (values: obj list) : obj = |
| | | 170 | | let _, args = split t values make |
| | | 171 | | Activator.CreateInstance(t, Array.ofList args) |
| | | 172 | | |
| | | 173 | | /// Reference-equality comparer for a pair of types: the default struct-tuple comparer boxes and |
| | | 174 | | /// costs ~100 ns per lookup, which the per-call conversion caches pay each time. |
| | | 175 | | /// Not part of the supported API: public only because compiled blocks call it, and it may change in any release. |
| | | 176 | | [<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>] |
| | 4 | 177 | | type TypePairComparer() = |
| | | 178 | | static member val Instance = TypePairComparer() |
| | | 179 | | interface System.Collections.Generic.IEqualityComparer<struct (Type * Type)> with |
| | 6134 | 180 | | member _.Equals(struct (a1, a2), struct (b1, b2)) = obj.ReferenceEquals(a1, b1) && obj.ReferenceEquals(a2, b2) |
| | 3726 | 181 | | member _.GetHashCode(struct (a, b)) = RuntimeHelpers.GetHashCode a * 31 + RuntimeHelpers.GetHashCode b |
| | | 182 | | |
| | | 183 | | /// A delegate type's own members. F# compiles a delegate at the *type's* accessibility, so an |
| | | 184 | | /// `internal` delegate has a non-public `Invoke` and constructor where C#'s stay public; asking for |
| | | 185 | | /// the public one gives null — a null-reference error far from the cause, or a rule that silently |
| | | 186 | | /// does not apply and C#'s own "invalid arguments" in its place (#121, #150). |
| | | 187 | | module internal DelegateMembers = |
| | | 188 | | let private flags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic |
| | | 189 | | let invokeOf (delegateType: Type) : MethodInfo = delegateType.GetMethod("Invoke", flags) |
| | | 190 | | /// Whether a delegate of this many parameters has a `Func`/`Action` (17 type parameters, the |
| | | 191 | | /// result among them); past it the delegate type is one emitted at run time. |
| | | 192 | | let funcFits (parameterCount: int) = parameterCount + 1 <= 17 |
| | | 193 | | let constructorOf (delegateType: Type) : ConstructorInfo = |
| | | 194 | | delegateType.GetConstructor(flags, null, [| typeof<obj>; typeof<nativeint> |], null) |
| | | 195 | | let onNetFramework = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith ".NET Framewo |
| | | 196 | | /// Whether C#'s binder would crash invoking a value of this type: .NET Framework's |
| | | 197 | | /// `Expression.Invoke` (which C#'s Invoke binder builds) finds `Invoke` by public lookup only, |
| | | 198 | | /// and throws a NullReferenceException for an F# `internal` delegate's (#151). |
| | | 199 | | let csharpCannotInvoke (t: Type) = |
| | | 200 | | onNetFramework && typeof<Delegate>.IsAssignableFrom t && (let i = invokeOf t in not (isNull i) && not i.IsPublic |
| | | 201 | | /// .NET Framework's `Expression.Lambda` finds a delegate type's `Invoke` by public lookup only, |
| | | 202 | | /// so a lambda at an F# `internal` delegate type fails there: the public delegate type of the |
| | | 203 | | /// same signature to compile it at instead (`Func`/`Action`, or one emitted past sixteen |
| | | 204 | | /// parameters), which `DelegateLiteral.From` rebinds (#125). None where no stand-in is needed, |
| | | 205 | | /// or for a byref signature (left as it was). |
| | | 206 | | let standIn (delegateType: Type) : Type option = |
| | | 207 | | let invoke = invokeOf delegateType |
| | | 208 | | let ps = invoke.GetParameters() |> Array.map (fun p -> p.ParameterType) |
| | | 209 | | if onNetFramework && not invoke.IsPublic && not (ps |> Array.exists (fun p -> p.IsByRef)) then |
| | | 210 | | Some(Expression.GetDelegateType(Array.append ps [| invoke.ReturnType |])) |
| | | 211 | | else None |
| | | 212 | | |
| | | 213 | | /// When an F# function and a delegate can stand for each other — one rule for both directions. |
| | | 214 | | /// The side that receives a value must accept it: the same type, or for reference types the |
| | | 215 | | /// variance delegates have (a parameter may take a more general type, a result may be a more |
| | | 216 | | /// specific one); no boxing, so value types match exactly. A `void` delegate is a `unit` result, |
| | | 217 | | /// and so is one returning `Unit` itself (`Func<int, unit>`). |
| | | 218 | | module internal Signatures = |
| | | 219 | | let private accepts (receiver: Type) (given: Type) = |
| | | 220 | | receiver = given || (not receiver.IsValueType && not given.IsValueType && receiver.IsAssignableFrom given) |
| | | 221 | | |
| | | 222 | | let private results (receiver: Type) (given: Type) (unitResult: bool) (isVoid: bool) = |
| | | 223 | | if isVoid then unitResult else accepts receiver given |
| | | 224 | | |
| | | 225 | | /// A function of `domains` → `result` serves as a delegate of `parameters` → `returns`: the |
| | | 226 | | /// delegate's arguments go to the function, its result comes back. A `unit` result serves |
| | | 227 | | /// only a `void` or `Unit`-returning delegate, as in F# (`fun x -> ()` is no `Func<int, obj>`). |
| | | 228 | | let functionServesDelegate (domains: Type list) (result: Type) (parameters: Type list) (returns: Type) = |
| | | 229 | | domains.Length = parameters.Length && List.forall2 accepts domains parameters |
| | | 230 | | && (if result = typeof<unit> then returns = typeof<Void> || returns = typeof<unit> |
| | | 231 | | else results returns result false (returns = typeof<Void>)) |
| | | 232 | | |
| | | 233 | | /// A delegate of `parameters` → `returns` serves as a function of `domains` → `result`: the |
| | | 234 | | /// function's arguments go to the delegate, its result comes back. |
| | | 235 | | let delegateServesFunction (domains: Type list) (result: Type) (parameters: Type list) (returns: Type) = |
| | | 236 | | domains.Length = parameters.Length && List.forall2 accepts parameters domains |
| | | 237 | | && results result returns (result = typeof<unit>) (returns = typeof<Void>) |
| | | 238 | | |
| | | 239 | | /// The same signature, no variance: what ranks a candidate among several a delegate or function |
| | | 240 | | /// argument fits (an exact match beats one through variance, as an exact type does). |
| | | 241 | | let exactly (domains: Type list) (result: Type) (parameters: Type list) (returns: Type) = |
| | | 242 | | domains = parameters && (returns = result || (returns = typeof<Void> && result = typeof<unit>)) |
| | | 243 | | |
| | | 244 | | /// Delegates over IL emitted once — `new Adapter(f)` and a delegate constructor, a rewrap — so a |
| | | 245 | | /// per-call conversion costs an allocation, not `ConstructorInfo.Invoke` or |
| | | 246 | | /// `Delegate.CreateDelegate`'s validation (~150–300 ns). One policy: `skipVisibility`, which covers |
| | | 247 | | /// a non-public member (an F# `internal` delegate's `Invoke` and constructor); and any failure |
| | | 248 | | /// falls back, not only an unsupported platform — the fallback is correct by construction, and a |
| | | 249 | | /// failure inside a static initializer would otherwise poison that type for the process. |
| | | 250 | | module internal Emit = |
| | | 251 | | let factory<'F when 'F :> Delegate> (name: string) (returnType: Type) (parameters: Type[]) (owner: Module) |
| | | 252 | | (emit: System.Reflection.Emit.ILGenerator -> unit) (fallback: unit -> 'F) : 'F = |
| | | 253 | | try |
| | | 254 | | let dm = System.Reflection.Emit.DynamicMethod(name, returnType, parameters, owner, true) |
| | | 255 | | emit (dm.GetILGenerator()) |
| | | 256 | | dm.CreateDelegate(typeof<'F>) :?> 'F |
| | | 257 | | with _ -> fallback () |