Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 2275 lines (1941 sloc) 108 KB
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.FSharp.Quotations
open System
open System.IO
open System.Reflection
open System.Collections.Generic
open Microsoft.FSharp
open Microsoft.FSharp.Core
open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
open Microsoft.FSharp.Core.Operators
open Microsoft.FSharp.Primitives.Basics
open Microsoft.FSharp.Collections
open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Core.Printf
open Microsoft.FSharp.Text.StructuredPrintfImpl
open Microsoft.FSharp.Text.StructuredPrintfImpl.LayoutOps
open Microsoft.FSharp.Text.StructuredPrintfImpl.TaggedTextOps
#nowarn "52" // The value has been copied to ensure the original is not mutated by this operation
#if FX_RESHAPED_REFLECTION
open PrimReflectionAdapters
open ReflectionAdapters
#endif
//--------------------------------------------------------------------------
// RAW quotations - basic data types
//--------------------------------------------------------------------------
module Helpers =
let qOneOrMoreRLinear q inp =
let rec queryAcc rvs e =
match q e with
| Some(v,body) -> queryAcc (v::rvs) body
| None ->
match rvs with
| [] -> None
| _ -> Some(List.rev rvs,e)
queryAcc [] inp
let qOneOrMoreLLinear q inp =
let rec queryAcc e rvs =
match q e with
| Some(body,v) -> queryAcc body (v::rvs)
| None ->
match rvs with
| [] -> None
| _ -> Some(e,rvs)
queryAcc inp []
let mkRLinear mk (vs,body) = List.foldBack (fun v acc -> mk(v,acc)) vs body
let mkLLinear mk (body,vs) = List.fold (fun acc v -> mk(acc,v)) body vs
let staticBindingFlags = BindingFlags.Static ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let staticOrInstanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Static ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
#if FX_RESHAPED_REFLECTION
let publicOrPrivateBindingFlags = true
#else
let publicOrPrivateBindingFlags = BindingFlags.Public ||| BindingFlags.NonPublic
#endif
let isDelegateType (typ:Type) =
if typ.IsSubclassOf(typeof<Delegate>) then
match typ.GetMethod("Invoke", instanceBindingFlags) with
| null -> false
| _ -> true
else
false
let getDelegateInvoke ty =
if not (isDelegateType(ty)) then invalidArg "ty" (SR.GetString(SR.delegateExpected))
ty.GetMethod("Invoke", instanceBindingFlags)
let inline checkNonNull argName (v: 'T) =
match box v with
| null -> nullArg argName
| _ -> ()
let getTypesFromParamInfos (infos : ParameterInfo[]) = infos |> Array.map (fun pi -> pi.ParameterType)
open Helpers
[<Sealed>]
[<CompiledName("FSharpVar")>]
[<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2218:OverrideGetHashCodeOnOverridingEquals",Justification="Equals override does not equate further objects, so default GetHashCode is still valid")>]
type Var(name: string, typ:Type, ?isMutable: bool) =
inherit obj()
static let getStamp =
let mutable lastStamp = -1L // first value retrieved will be 0
fun () -> System.Threading.Interlocked.Increment &lastStamp
static let globals = new Dictionary<(string*Type),Var>(11)
let stamp = getStamp ()
let isMutable = defaultArg isMutable false
member v.Name = name
member v.IsMutable = isMutable
member v.Type = typ
member v.Stamp = stamp
static member Global(name,typ: Type) =
checkNonNull "name" name
checkNonNull "typ" typ
lock globals (fun () ->
let mutable res = Unchecked.defaultof<Var>
let ok = globals.TryGetValue((name,typ),&res)
if ok then res else
let res = new Var(name,typ)
globals.[(name,typ)] <- res
res)
override v.ToString() = name
override v.GetHashCode() = base.GetHashCode()
override v.Equals(obj:obj) =
match obj with
| :? Var as v2 -> System.Object.ReferenceEquals(v,v2)
| _ -> false
interface System.IComparable with
member v.CompareTo(obj:obj) =
match obj with
| :? Var as v2 ->
if System.Object.ReferenceEquals(v,v2) then 0 else
let c = compare v.Name v2.Name
if c <> 0 then c else
#if !FX_NO_REFLECTION_METADATA_TOKENS // not available on Compact Framework
let c = compare v.Type.MetadataToken v2.Type.MetadataToken
if c <> 0 then c else
let c = compare v.Type.Module.MetadataToken v2.Type.Module.MetadataToken
if c <> 0 then c else
#endif
let c = compare v.Type.Assembly.FullName v2.Type.Assembly.FullName
if c <> 0 then c else
compare v.Stamp v2.Stamp
| _ -> 0
/// Represents specifications of a subset of F# expressions
[<StructuralEquality; NoComparison>]
type Tree =
| CombTerm of ExprConstInfo * Expr list
| VarTerm of Var
| LambdaTerm of Var * Expr
| HoleTerm of Type * int
and
[<StructuralEquality; NoComparison>]
ExprConstInfo =
| AppOp
| IfThenElseOp
| LetRecOp
| LetRecCombOp
| LetOp
| NewRecordOp of Type
| NewUnionCaseOp of UnionCaseInfo
| UnionCaseTestOp of UnionCaseInfo
| NewTupleOp of Type
| TupleGetOp of Type * int
| InstancePropGetOp of PropertyInfo
| StaticPropGetOp of PropertyInfo
| InstancePropSetOp of PropertyInfo
| StaticPropSetOp of PropertyInfo
| InstanceFieldGetOp of FieldInfo
| StaticFieldGetOp of FieldInfo
| InstanceFieldSetOp of FieldInfo
| StaticFieldSetOp of FieldInfo
| NewObjectOp of ConstructorInfo
| InstanceMethodCallOp of MethodInfo
| StaticMethodCallOp of MethodInfo
| CoerceOp of Type
| NewArrayOp of Type
| NewDelegateOp of Type
| QuoteOp of bool
| SequentialOp
| AddressOfOp
| VarSetOp
| AddressSetOp
| TypeTestOp of Type
| TryWithOp
| TryFinallyOp
| ForIntegerRangeLoopOp
| WhileLoopOp
// Arbitrary spliced values - not serialized
| ValueOp of obj * Type * string option
| WithValueOp of obj * Type
| DefaultValueOp of Type
and [<CompiledName("FSharpExpr")>]
Expr(term:Tree,attribs:Expr list) =
member x.Tree = term
member x.CustomAttributes = attribs
override x.Equals(obj) =
match obj with
| :? Expr as y ->
let rec eq t1 t2 =
match t1, t2 with
// We special-case ValueOp to ensure that ValueWithName = Value
| CombTerm(ValueOp(v1,ty1,_),[]),CombTerm(ValueOp(v2,ty2,_),[]) -> (v1 = v2) && (ty1 = ty2)
| CombTerm(c1, es1), CombTerm(c2,es2) -> c1 = c2 && es1.Length = es2.Length && (es1 = es2)
| VarTerm v1, VarTerm v2 -> (v1 = v2)
| LambdaTerm (v1,e1), LambdaTerm(v2,e2) -> (v1 = v2) && (e1 = e2)
| HoleTerm (ty1,n1), HoleTerm(ty2,n2) -> (ty1 = ty2) && (n1 = n2)
| _ -> false
eq x.Tree y.Tree
| _ -> false
override x.GetHashCode() =
x.Tree.GetHashCode()
override x.ToString() = x.ToString(false)
member x.ToString(full) =
Microsoft.FSharp.Text.StructuredPrintfImpl.Display.layout_to_string Microsoft.FSharp.Text.StructuredPrintfImpl.FormatOptions.Default (x.GetLayout(full))
member x.GetLayout(long) =
let expr (e:Expr ) = e.GetLayout(long)
let exprs (es:Expr list) = es |> List.map expr
let parens ls = bracketL (commaListL ls)
let pairL l1 l2 = bracketL (l1 ^^ sepL Literals.comma ^^ l2)
let listL ls = squareBracketL (commaListL ls)
let combTaggedL nm ls = wordL nm ^^ parens ls
let combL nm ls = combTaggedL (tagKeyword nm) ls
let noneL = wordL (tagProperty "None")
let someL e = combTaggedL (tagMethod "Some") [expr e]
let typeL (o: Type) = wordL (tagClass (if long then o.FullName else o.Name))
let objL (o: 'T) = wordL (tagText (sprintf "%A" o))
let varL (v:Var) = wordL (tagLocal v.Name)
let (|E|) (e: Expr) = e.Tree
let (|Lambda|_|) (E x) = match x with LambdaTerm(a,b) -> Some (a,b) | _ -> None
let (|IteratedLambda|_|) (e: Expr) = qOneOrMoreRLinear (|Lambda|_|) e
let ucaseL (unionCase:UnionCaseInfo) = (if long then objL unionCase else wordL (tagUnionCase unionCase.Name))
let minfoL (minfo: MethodInfo) = if long then objL minfo else wordL (tagMethod minfo.Name)
let cinfoL (cinfo: ConstructorInfo) = if long then objL cinfo else wordL (tagMethod cinfo.DeclaringType.Name)
let pinfoL (pinfo: PropertyInfo) = if long then objL pinfo else wordL (tagProperty pinfo.Name)
let finfoL (finfo: FieldInfo) = if long then objL finfo else wordL (tagField finfo.Name)
let rec (|NLambdas|_|) n (e:Expr) =
match e with
| _ when n <= 0 -> Some([],e)
| Lambda(v,NLambdas ((-) n 1) (vs,b)) -> Some(v::vs,b)
| _ -> None
match x.Tree with
| CombTerm(AppOp,args) -> combL "Application" (exprs args)
| CombTerm(IfThenElseOp,args) -> combL "IfThenElse" (exprs args)
| CombTerm(LetRecOp,[IteratedLambda(vs,E(CombTerm(LetRecCombOp,b2::bs)))]) -> combL "LetRecursive" [listL (List.map2 pairL (List.map varL vs) (exprs bs) ); b2.GetLayout(long)]
| CombTerm(LetOp,[e;E(LambdaTerm(v,b))]) -> combL "Let" [varL v; e.GetLayout(long); b.GetLayout(long)]
| CombTerm(NewRecordOp(ty),args) -> combL "NewRecord" (typeL ty :: exprs args)
| CombTerm(NewUnionCaseOp(unionCase),args) -> combL "NewUnionCase" (ucaseL unionCase :: exprs args)
| CombTerm(UnionCaseTestOp(unionCase),args) -> combL "UnionCaseTest" (exprs args@ [ucaseL unionCase])
| CombTerm(NewTupleOp _,args) -> combL "NewTuple" (exprs args)
| CombTerm(TupleGetOp (_,i),[arg]) -> combL "TupleGet" ([expr arg] @ [objL i])
| CombTerm(ValueOp(v,_,Some nm),[]) -> combL "ValueWithName" [objL v; wordL (tagLocal nm)]
| CombTerm(ValueOp(v,_,None),[]) -> combL "Value" [objL v]
| CombTerm(WithValueOp(v,_),[defn]) -> combL "WithValue" [objL v; expr defn]
| CombTerm(InstanceMethodCallOp(minfo),obj::args) -> combL "Call" [someL obj; minfoL minfo; listL (exprs args)]
| CombTerm(StaticMethodCallOp(minfo),args) -> combL "Call" [noneL; minfoL minfo; listL (exprs args)]
| CombTerm(InstancePropGetOp(pinfo),(obj::args)) -> combL "PropertyGet" [someL obj; pinfoL pinfo; listL (exprs args)]
| CombTerm(StaticPropGetOp(pinfo),args) -> combL "PropertyGet" [noneL; pinfoL pinfo; listL (exprs args)]
| CombTerm(InstancePropSetOp(pinfo),(obj::args)) -> combL "PropertySet" [someL obj; pinfoL pinfo; listL (exprs args)]
| CombTerm(StaticPropSetOp(pinfo),args) -> combL "PropertySet" [noneL; pinfoL pinfo; listL (exprs args)]
| CombTerm(InstanceFieldGetOp(finfo),[obj]) -> combL "FieldGet" [someL obj; finfoL finfo]
| CombTerm(StaticFieldGetOp(finfo),[]) -> combL "FieldGet" [noneL; finfoL finfo]
| CombTerm(InstanceFieldSetOp(finfo),[obj;v]) -> combL "FieldSet" [someL obj; finfoL finfo; expr v;]
| CombTerm(StaticFieldSetOp(finfo),[v]) -> combL "FieldSet" [noneL; finfoL finfo; expr v;]
| CombTerm(CoerceOp(ty),[arg]) -> combL "Coerce" [ expr arg; typeL ty]
| CombTerm(NewObjectOp cinfo,args) -> combL "NewObject" ([ cinfoL cinfo ] @ exprs args)
| CombTerm(DefaultValueOp(ty),args) -> combL "DefaultValue" ([ typeL ty ] @ exprs args)
| CombTerm(NewArrayOp(ty),args) -> combL "NewArray" ([ typeL ty ] @ exprs args)
| CombTerm(TypeTestOp(ty),args) -> combL "TypeTest" ([ typeL ty] @ exprs args)
| CombTerm(AddressOfOp,args) -> combL "AddressOf" (exprs args)
| CombTerm(VarSetOp,[E(VarTerm(v)); e]) -> combL "VarSet" [varL v; expr e]
| CombTerm(AddressSetOp,args) -> combL "AddressSet" (exprs args)
| CombTerm(ForIntegerRangeLoopOp,[e1;e2;E(LambdaTerm(v,e3))]) -> combL "ForIntegerRangeLoop" [varL v; expr e1; expr e2; expr e3]
| CombTerm(WhileLoopOp,args) -> combL "WhileLoop" (exprs args)
| CombTerm(TryFinallyOp,args) -> combL "TryFinally" (exprs args)
| CombTerm(TryWithOp,[e1;Lambda(v1,e2);Lambda(v2,e3)]) -> combL "TryWith" [expr e1; varL v1; expr e2; varL v2; expr e3]
| CombTerm(SequentialOp,args) -> combL "Sequential" (exprs args)
| CombTerm(NewDelegateOp(ty),[e]) ->
let nargs = (getDelegateInvoke ty).GetParameters().Length
if nargs = 0 then
match e with
| NLambdas 1 ([_],e) -> combL "NewDelegate" ([typeL ty] @ [expr e])
| NLambdas 0 ([],e) -> combL "NewDelegate" ([typeL ty] @ [expr e])
| _ -> combL "NewDelegate" [typeL ty; expr e]
else
match e with
| NLambdas nargs (vs,e) -> combL "NewDelegate" ([typeL ty] @ (vs |> List.map varL) @ [expr e])
| _ -> combL "NewDelegate" [typeL ty; expr e]
//| CombTerm(_,args) -> combL "??" (exprs args)
| VarTerm(v) -> wordL (tagLocal v.Name)
| LambdaTerm(v,b) -> combL "Lambda" [varL v; expr b]
| HoleTerm _ -> wordL (tagLocal "_")
| CombTerm(QuoteOp _,args) -> combL "Quote" (exprs args)
| _ -> failwithf "Unexpected term in layout %A" x.Tree
and [<CompiledName("FSharpExpr`1")>]
Expr<'T>(term:Tree,attribs) =
inherit Expr(term,attribs)
member x.Raw = (x :> Expr)
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Patterns =
/// Internal type representing a deserialized object that is yet to be instantiated. Representation is
/// as a computation.
type Instantiable<'T> = (int -> Type) -> 'T
type ByteStream(bytes:byte[], initial:int, len:int) =
let mutable pos = initial
let lim = initial + len
member b.ReadByte() =
if pos >= lim then failwith "end of stream";
let res = int32 bytes.[pos]
pos <- pos + 1;
res
member b.ReadBytes n =
if pos + n > lim then failwith "ByteStream.ReadBytes: end of stream";
let res = bytes.[pos..pos+n-1]
pos <- pos + n;
res
member b.ReadUtf8BytesAsString n =
let res = System.Text.Encoding.UTF8.GetString(bytes,pos,n)
pos <- pos + n;
res
let E t = new Expr< >(t,[])
let EA (t,attribs) = new Expr< >(t,attribs)
let ES ts = List.map E ts
let (|E|) (e: Expr) = e.Tree
let (|ES|) (es: list<Expr>) = es |> List.map (fun e -> e.Tree)
let (|FrontAndBack|_|) es =
let rec loop acc xs = match xs with [] -> None | [h] -> Some (List.rev acc, h) | h::t -> loop (h::acc) t
loop [] es
let funTyC = typeof<(obj -> obj)>.GetGenericTypeDefinition()
let exprTyC = typedefof<Expr<int>>
let voidTy = typeof<System.Void>
let unitTy = typeof<unit>
let removeVoid a = if a = voidTy then unitTy else a
let addVoid a = if a = unitTy then voidTy else a
let mkFunTy a b =
let (a, b) = removeVoid a, removeVoid b
funTyC.MakeGenericType([| a;b |])
let mkArrayTy (t:Type) = t.MakeArrayType();
let mkExprTy (t:Type) = exprTyC.MakeGenericType([| t |])
let rawExprTy = typeof<Expr>
//--------------------------------------------------------------------------
// Active patterns for decomposing quotations
//--------------------------------------------------------------------------
let (|Comb0|_|) (E x) = match x with CombTerm(k,[]) -> Some(k) | _ -> None
let (|Comb1|_|) (E x) = match x with CombTerm(k,[x]) -> Some(k,x) | _ -> None
let (|Comb2|_|) (E x) = match x with CombTerm(k,[x1;x2]) -> Some(k,x1,x2) | _ -> None
let (|Comb3|_|) (E x) = match x with CombTerm(k,[x1;x2;x3]) -> Some(k,x1,x2,x3) | _ -> None
[<CompiledName("VarPattern")>]
let (|Var|_|) (E x) = match x with VarTerm v -> Some v | _ -> None
[<CompiledName("ApplicationPattern")>]
let (|Application|_|) input = match input with Comb2(AppOp,a,b) -> Some (a,b) | _ -> None
[<CompiledName("LambdaPattern")>]
let (|Lambda|_|) (E x) = match x with LambdaTerm(a,b) -> Some (a,b) | _ -> None
[<CompiledName("QuotePattern")>]
let (|Quote|_|) (E x) = match x with CombTerm(QuoteOp _,[a]) -> Some (a) | _ -> None
[<CompiledName("QuoteRawPattern")>]
let (|QuoteRaw|_|) (E x) = match x with CombTerm(QuoteOp false,[a]) -> Some (a) | _ -> None
[<CompiledName("QuoteTypedPattern")>]
let (|QuoteTyped|_|) (E x) = match x with CombTerm(QuoteOp true,[a]) -> Some (a) | _ -> None
[<CompiledName("IfThenElsePattern")>]
let (|IfThenElse|_|) input = match input with Comb3(IfThenElseOp,e1,e2,e3) -> Some(e1,e2,e3) | _ -> None
[<CompiledName("NewTuplePattern")>]
let (|NewTuple|_|) input = match input with E(CombTerm(NewTupleOp(_),es)) -> Some(es) | _ -> None
[<CompiledName("DefaultValuePattern")>]
let (|DefaultValue|_|) input = match input with E(CombTerm(DefaultValueOp(ty),[])) -> Some(ty) | _ -> None
[<CompiledName("NewRecordPattern")>]
let (|NewRecord|_|) input = match input with E(CombTerm(NewRecordOp(x),es)) -> Some(x,es) | _ -> None
[<CompiledName("NewUnionCasePattern")>]
let (|NewUnionCase|_|) input = match input with E(CombTerm(NewUnionCaseOp(unionCase),es)) -> Some(unionCase,es) | _ -> None
[<CompiledName("UnionCaseTestPattern")>]
let (|UnionCaseTest|_|) input = match input with Comb1(UnionCaseTestOp(unionCase),e) -> Some(e,unionCase) | _ -> None
[<CompiledName("TupleGetPattern")>]
let (|TupleGet|_|) input = match input with Comb1(TupleGetOp(_,n),e) -> Some(e,n) | _ -> None
[<CompiledName("CoercePattern")>]
let (|Coerce|_|) input = match input with Comb1(CoerceOp ty,e1) -> Some(e1,ty) | _ -> None
[<CompiledName("TypeTestPattern")>]
let (|TypeTest|_|) input = match input with Comb1(TypeTestOp ty,e1) -> Some(e1,ty) | _ -> None
[<CompiledName("NewArrayPattern")>]
let (|NewArray|_|) input = match input with E(CombTerm(NewArrayOp ty,es)) -> Some(ty,es) | _ -> None
[<CompiledName("AddressSetPattern")>]
let (|AddressSet|_|) input = match input with E(CombTerm(AddressSetOp,[e;v])) -> Some(e,v) | _ -> None
[<CompiledName("TryFinallyPattern")>]
let (|TryFinally|_|) input = match input with E(CombTerm(TryFinallyOp,[e1;e2])) -> Some(e1,e2) | _ -> None
[<CompiledName("TryWithPattern")>]
let (|TryWith|_|) input = match input with E(CombTerm(TryWithOp,[e1;Lambda(v1,e2);Lambda(v2,e3)])) -> Some(e1,v1,e2,v2,e3) | _ -> None
[<CompiledName("VarSetPattern")>]
let (|VarSet|_| ) input = match input with E(CombTerm(VarSetOp,[E(VarTerm(v)); e])) -> Some(v,e) | _ -> None
[<CompiledName("ValuePattern")>]
let (|Value|_|) input = match input with E(CombTerm(ValueOp (v,ty,_),_)) -> Some(v,ty) | _ -> None
[<CompiledName("ValueObjPattern")>]
let (|ValueObj|_|) input = match input with E(CombTerm(ValueOp (v,_,_),_)) -> Some(v) | _ -> None
[<CompiledName("ValueWithNamePattern")>]
let (|ValueWithName|_|) input =
match input with
| E(CombTerm(ValueOp (v,ty,Some nm),_)) -> Some(v,ty,nm)
| _ -> None
[<CompiledName("WithValuePattern")>]
let (|WithValue|_|) input =
match input with
| E(CombTerm(WithValueOp (v,ty),[e])) -> Some(v,ty,e)
| _ -> None
[<CompiledName("AddressOfPattern")>]
let (|AddressOf|_|) input =
match input with
| Comb1(AddressOfOp,e) -> Some(e)
| _ -> None
[<CompiledName("SequentialPattern")>]
let (|Sequential|_|) input =
match input with
| Comb2(SequentialOp,e1,e2) -> Some(e1,e2)
| _ -> None
[<CompiledName("ForIntegerRangeLoopPattern")>]
let (|ForIntegerRangeLoop|_|) input =
match input with
| Comb3(ForIntegerRangeLoopOp,e1,e2,Lambda(v, e3)) -> Some(v,e1,e2,e3)
| _ -> None
[<CompiledName("WhileLoopPattern")>]
let (|WhileLoop|_|) input =
match input with
| Comb2(WhileLoopOp,e1,e2) -> Some(e1,e2)
| _ -> None
[<CompiledName("PropertyGetPattern")>]
let (|PropertyGet|_|) input =
match input with
| E(CombTerm(StaticPropGetOp pinfo,args)) -> Some(None,pinfo,args)
| E(CombTerm(InstancePropGetOp pinfo,obj::args)) -> Some(Some(obj),pinfo,args)
| _ -> None
[<CompiledName("PropertySetPattern")>]
let (|PropertySet|_|) input =
match input with
| E(CombTerm(StaticPropSetOp pinfo, FrontAndBack(args,v))) -> Some(None,pinfo,args,v)
| E(CombTerm(InstancePropSetOp pinfo, obj::FrontAndBack(args,v))) -> Some(Some(obj),pinfo,args,v)
| _ -> None
[<CompiledName("FieldGetPattern")>]
let (|FieldGet|_|) input =
match input with
| E(CombTerm(StaticFieldGetOp finfo,[])) -> Some(None,finfo)
| E(CombTerm(InstanceFieldGetOp finfo,[obj])) -> Some(Some(obj),finfo)
| _ -> None
[<CompiledName("FieldSetPattern")>]
let (|FieldSet|_|) input =
match input with
| E(CombTerm(StaticFieldSetOp finfo,[v])) -> Some(None,finfo,v)
| E(CombTerm(InstanceFieldSetOp finfo,[obj;v])) -> Some(Some(obj),finfo,v)
| _ -> None
[<CompiledName("NewObjectPattern")>]
let (|NewObject|_|) input =
match input with
| E(CombTerm(NewObjectOp ty,e)) -> Some(ty,e) | _ -> None
[<CompiledName("CallPattern")>]
let (|Call|_|) input =
match input with
| E(CombTerm(StaticMethodCallOp minfo,args)) -> Some(None,minfo,args)
| E(CombTerm(InstanceMethodCallOp minfo,(obj::args))) -> Some(Some(obj),minfo,args)
| _ -> None
let (|LetRaw|_|) input =
match input with
| Comb2(LetOp,e1,e2) -> Some(e1,e2)
| _ -> None
let (|LetRecRaw|_|) input =
match input with
| Comb1(LetRecOp,e1) -> Some(e1)
| _ -> None
[<CompiledName("LetPattern")>]
let (|Let|_|)input =
match input with
| LetRaw(e,Lambda(v,body)) -> Some(v,e,body)
| _ -> None
let (|IteratedLambda|_|) (e: Expr) = qOneOrMoreRLinear (|Lambda|_|) e
let rec (|NLambdas|_|) n (e:Expr) =
match e with
| _ when n <= 0 -> Some([],e)
| Lambda(v,NLambdas ((-) n 1) (vs,b)) -> Some(v::vs,b)
| _ -> None
[<CompiledName("NewDelegatePattern")>]
let (|NewDelegate|_|) input =
match input with
| Comb1(NewDelegateOp(ty),e) ->
let nargs = (getDelegateInvoke ty).GetParameters().Length
if nargs = 0 then
match e with
| NLambdas 1 ([_],e) -> Some(ty,[],e) // try to strip the unit parameter if there is one
| NLambdas 0 ([],e) -> Some(ty,[],e)
| _ -> None
else
match e with
| NLambdas nargs (vs,e) -> Some(ty,vs,e)
| _ -> None
| _ -> None
[<CompiledName("LetRecursivePattern")>]
let (|LetRecursive|_|) input =
match input with
| LetRecRaw(IteratedLambda(vs1,E(CombTerm(LetRecCombOp,body::es)))) -> Some(List.zip vs1 es,body)
| _ -> None
//--------------------------------------------------------------------------
// Getting the type of Raw quotations
//--------------------------------------------------------------------------
// Returns record member specified by name
let getRecordProperty(ty,fieldName) =
let mems = FSharpType.GetRecordFields(ty,publicOrPrivateBindingFlags)
match mems |> Array.tryFind (fun minfo -> minfo.Name = fieldName) with
| Some (m) -> m
| _ -> invalidArg "fieldName" (String.Format(SR.GetString(SR.QmissingRecordField), ty.FullName, fieldName))
let getUnionCaseInfo(ty,unionCaseName) =
let cases = FSharpType.GetUnionCases(ty,publicOrPrivateBindingFlags)
match cases |> Array.tryFind (fun ucase -> ucase.Name = unionCaseName) with
| Some(case) -> case
| _ -> invalidArg "unionCaseName" (String.Format(SR.GetString(SR.QmissingUnionCase), ty.FullName, unionCaseName))
let getUnionCaseInfoField(unionCase:UnionCaseInfo,index) =
let fields = unionCase.GetFields()
if index < 0 || index >= fields.Length then invalidArg "index" (SR.GetString(SR.QinvalidCaseIndex))
fields.[index]
/// Returns type of lambda application - something like "(fun a -> ..) b"
let rec typeOfAppliedLambda f =
let fty = ((typeOf f):Type)
match fty.GetGenericArguments() with
| [| _; b|] -> b
| _ -> raise <| System.InvalidOperationException (SR.GetString(SR.QillFormedAppOrLet))
/// Returns type of the Raw quotation or fails if the quotation is ill formed
/// if 'verify' is true, verifies all branches, otherwise ignores some of them when not needed
and typeOf<'T when 'T :> Expr> (e : 'T) : Type =
let (E t) = e
match t with
| VarTerm v -> v.Type
| LambdaTerm (v,b) -> mkFunTy v.Type (typeOf b)
| HoleTerm (ty,_) -> ty
| CombTerm (c,args) ->
match c,args with
| AppOp,[f;_] -> typeOfAppliedLambda f
| LetOp,_ -> match e with Let(_,_,b) -> typeOf b | _ -> failwith "unreachable"
| IfThenElseOp,[_;t;_] -> typeOf t
| LetRecOp,_ -> match e with LetRecursive(_,b) -> typeOf b | _ -> failwith "unreachable"
| LetRecCombOp,_ -> failwith "typeOfConst: LetRecCombOp"
| NewRecordOp ty,_ -> ty
| NewUnionCaseOp unionCase,_ -> unionCase.DeclaringType
| UnionCaseTestOp _,_ -> typeof<Boolean>
| ValueOp (_, ty, _),_ -> ty
| WithValueOp (_, ty),_ -> ty
| TupleGetOp (ty,i),_ -> FSharpType.GetTupleElements(ty).[i]
| NewTupleOp ty,_ -> ty
| StaticPropGetOp prop,_ -> prop.PropertyType
| InstancePropGetOp prop,_ -> prop.PropertyType
| StaticPropSetOp _,_ -> typeof<Unit>
| InstancePropSetOp _,_ -> typeof<Unit>
| InstanceFieldGetOp fld ,_ -> fld.FieldType
| StaticFieldGetOp fld ,_ -> fld.FieldType
| InstanceFieldSetOp _,_ -> typeof<Unit>
| StaticFieldSetOp _,_ -> typeof<Unit>
| NewObjectOp ctor,_ -> ctor.DeclaringType
| InstanceMethodCallOp minfo,_ -> minfo.ReturnType |> removeVoid
| StaticMethodCallOp minfo,_ -> minfo.ReturnType |> removeVoid
| CoerceOp ty,_ -> ty
| SequentialOp,[_;b] -> typeOf b
| ForIntegerRangeLoopOp,_ -> typeof<Unit>
| NewArrayOp ty,_ -> mkArrayTy ty
| NewDelegateOp ty,_ -> ty
| DefaultValueOp ty,_ -> ty
| TypeTestOp _,_ -> typeof<bool>
| QuoteOp true,[expr] -> mkExprTy (typeOf expr)
| QuoteOp false,[_] -> rawExprTy
| TryFinallyOp,[e1;_] -> typeOf e1
| TryWithOp,[e1;_;_] -> typeOf e1
| WhileLoopOp,_
| VarSetOp,_
| AddressSetOp,_ -> typeof<Unit>
| AddressOfOp,[expr]-> (typeOf expr).MakeByRefType()
| (AddressOfOp | QuoteOp _ | SequentialOp | TryWithOp | TryFinallyOp | IfThenElseOp | AppOp),_ -> failwith "unreachable"
//--------------------------------------------------------------------------
// Constructors for building Raw quotations
//--------------------------------------------------------------------------
let mkFEN op l = E(CombTerm(op,l))
let mkFE0 op = E(CombTerm(op,[]))
let mkFE1 op x = E(CombTerm(op,[(x:>Expr)]))
let mkFE2 op (x,y) = E(CombTerm(op,[(x:>Expr);(y:>Expr)]))
let mkFE3 op (x,y,z) = E(CombTerm(op,[(x:>Expr);(y:>Expr);(z:>Expr)]) )
let mkOp v () = v
//--------------------------------------------------------------------------
// Type-checked constructors for building Raw quotations
//--------------------------------------------------------------------------
// t2 is inherited from t1 / t2 implements interface t1 or t2 == t1
let assignableFrom (t1:Type) (t2:Type) =
t1.IsAssignableFrom(t2)
let checkTypesSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) =
if (expectedType <> receivedType) then
invalidArg "receivedType" (String.Format(threeHoleSR, name, expectedType, receivedType))
let checkTypesWeakSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) =
if (not (assignableFrom expectedType receivedType)) then
invalidArg "receivedType" (String.Format(threeHoleSR, name, expectedType, receivedType))
let checkArgs (paramInfos: ParameterInfo[]) (args:list<Expr>) =
if (paramInfos.Length <> args.Length) then invalidArg "args" (SR.GetString(SR.QincorrectNumArgs))
List.iter2
( fun (p:ParameterInfo) a -> checkTypesWeakSR p.ParameterType (typeOf a) "args" (SR.GetString(SR.QtmmInvalidParam)))
(paramInfos |> Array.toList)
args
// todo: shouldn't this be "strong" type check? sometimes?
let checkAssignableFrom ty1 ty2 =
if not (assignableFrom ty1 ty2) then invalidArg "ty2" (SR.GetString(SR.QincorrectType))
let checkObj (membInfo: MemberInfo) (obj: Expr) =
// The MemberInfo may be a property associated with a union
// find the actual related union type
let rec loop (ty:Type) = if FSharpType.IsUnion ty && FSharpType.IsUnion ty.BaseType then loop ty.BaseType else ty
let declType = loop membInfo.DeclaringType
if not (assignableFrom declType (typeOf obj)) then invalidArg "obj" (SR.GetString(SR.QincorrectInstanceType))
// Checks lambda application for correctness
let checkAppliedLambda (f, v) =
let fty = typeOf f
let ftyG = (if fty.IsGenericType then fty.GetGenericTypeDefinition() else fty)
checkTypesSR funTyC ftyG "f" (SR.GetString(SR.QtmmExpectedFunction))
let vty = (typeOf v)
match fty.GetGenericArguments() with
| [| a; _ |] -> checkTypesSR vty a "f" (SR.GetString(SR.QtmmFunctionArgTypeMismatch))
| _ -> invalidArg "f" (SR.GetString(SR.QinvalidFuncType))
// Returns option (by name) of a NewUnionCase type
let getUnionCaseFields ty str =
let cases = FSharpType.GetUnionCases(ty,publicOrPrivateBindingFlags)
match cases |> Array.tryFind (fun ucase -> ucase.Name = str) with
| Some(case) -> case.GetFields()
| _ -> invalidArg "ty" (String.Format(SR.GetString(SR.notAUnionType), ty.FullName))
let checkBind(v:Var,e) =
let ety = typeOf e
checkTypesSR v.Type ety "let" (SR.GetString(SR.QtmmVarTypeNotMatchRHS))
// [Correct by definition]
let mkVar v = E(VarTerm v )
let mkQuote(a,isTyped) = E(CombTerm(QuoteOp isTyped,[(a:>Expr)] ))
let mkValue (v,ty) = mkFE0 (ValueOp(v,ty,None))
let mkValueWithName (v,ty,nm) = mkFE0 (ValueOp(v,ty,Some nm))
let mkValueWithDefn (v,ty,defn) = mkFE1 (WithValueOp(v,ty)) defn
let mkValueG (v:'T) = mkValue(box v, typeof<'T>)
let mkLiftedValueOpG (v, ty: System.Type) =
let obj = if ty.IsEnum then System.Enum.ToObject(ty, box v) else box v
ValueOp(obj, ty, None)
let mkUnit () = mkValue(null, typeof<unit>)
let mkAddressOf v = mkFE1 AddressOfOp v
let mkSequential (e1,e2) = mkFE2 SequentialOp (e1,e2)
let mkTypeTest (e,ty) = mkFE1 (TypeTestOp(ty)) e
let mkVarSet (v,e) = mkFE2 VarSetOp (mkVar(v),e)
let mkAddressSet (e1,e2) = mkFE2 AddressSetOp (e1,e2)
let mkLambda(var,body) = E(LambdaTerm(var,(body:>Expr)))
let mkTryWith(e1,v1,e2,v2,e3) = mkFE3 TryWithOp (e1,mkLambda(v1,e2),mkLambda(v2,e3))
let mkTryFinally(e1,e2) = mkFE2 TryFinallyOp (e1,e2)
let mkCoerce (ty,x) = mkFE1 (CoerceOp ty) x
let mkNull (ty) = mkFE0 (ValueOp(null,ty,None))
let mkApplication v = checkAppliedLambda v; mkFE2 AppOp v
let mkLetRaw v =
mkFE2 LetOp v
let mkLetRawWithCheck ((e1,e2) as v) =
checkAppliedLambda (e2,e1)
mkLetRaw v
// Tuples
let mkNewTupleWithType (ty,args:Expr list) =
let mems = FSharpType.GetTupleElements ty |> Array.toList
if (args.Length <> mems.Length) then invalidArg "args" (SR.GetString(SR.QtupleLengthsDiffer))
List.iter2(fun mt a -> checkTypesSR mt (typeOf a) "args" (SR.GetString(SR.QtmmTuple)) ) mems args
mkFEN (NewTupleOp ty) args
let mkNewTuple (args) =
let ty = FSharpType.MakeTupleType(Array.map typeOf (Array.ofList args))
mkFEN (NewTupleOp ty) args
let mkTupleGet (ty,n,x) =
checkTypesSR ty (typeOf x) "tupleGet" (SR.GetString(SR.QtmmExprNotMatchTuple))
let mems = FSharpType.GetTupleElements ty
if (n < 0 || mems.Length <= n) then invalidArg "n" (SR.GetString(SR.QtupleAccessOutOfRange))
mkFE1 (TupleGetOp (ty,n)) x
// Records
let mkNewRecord (ty,args:list<Expr>) =
let mems = FSharpType.GetRecordFields(ty,publicOrPrivateBindingFlags)
if (args.Length <> mems.Length) then invalidArg "args" (SR.GetString(SR.QincompatibleRecordLength))
List.iter2 (fun (minfo:PropertyInfo) a -> checkTypesSR minfo.PropertyType (typeOf a) "recd" (SR.GetString(SR.QtmmIncorrectArgForRecord))) (Array.toList mems) args
mkFEN (NewRecordOp ty) args
// Discriminated unions
let mkNewUnionCase (unionCase:UnionCaseInfo,args:list<Expr>) =
if Unchecked.defaultof<UnionCaseInfo> = unionCase then raise (new ArgumentNullException())
let sargs = unionCase.GetFields()
if (args.Length <> sargs.Length) then invalidArg "args" (SR.GetString(SR.QunionNeedsDiffNumArgs))
List.iter2 (fun (minfo:PropertyInfo) a -> checkTypesSR minfo.PropertyType (typeOf a) "sum" (SR.GetString(SR.QtmmIncorrectArgForUnion))) (Array.toList sargs) args
mkFEN (NewUnionCaseOp unionCase) args
let mkUnionCaseTest (unionCase:UnionCaseInfo,expr) =
if Unchecked.defaultof<UnionCaseInfo> = unionCase then raise (new ArgumentNullException())
checkTypesSR unionCase.DeclaringType (typeOf expr) "UnionCaseTagTest" (SR.GetString(SR.QtmmExprTypeMismatch))
mkFE1 (UnionCaseTestOp unionCase) expr
// Conditional etc..
let mkIfThenElse (e,t,f) =
checkTypesSR (typeOf t) (typeOf f) "cond" (SR.GetString(SR.QtmmTrueAndFalseMustMatch))
checkTypesSR (typeof<Boolean>) (typeOf e) "cond" (SR.GetString(SR.QtmmCondMustBeBool))
mkFE3 IfThenElseOp (e,t,f)
let mkNewArray (ty,args) =
List.iter (fun a -> checkTypesSR ty (typeOf a) "newArray" (SR.GetString(SR.QtmmInitArray))) args
mkFEN (NewArrayOp ty) args
let mkInstanceFieldGet(obj,finfo:FieldInfo) =
if Unchecked.defaultof<FieldInfo> = finfo then raise (new ArgumentNullException())
match finfo.IsStatic with
| false ->
checkObj finfo obj
mkFE1 (InstanceFieldGetOp finfo) obj
| true -> invalidArg "finfo" (SR.GetString(SR.QstaticWithReceiverObject))
let mkStaticFieldGet (finfo:FieldInfo) =
if Unchecked.defaultof<FieldInfo> = finfo then raise (new ArgumentNullException())
match finfo.IsStatic with
| true -> mkFE0 (StaticFieldGetOp finfo)
| false -> invalidArg "finfo" (SR.GetString(SR.QnonStaticNoReceiverObject))
let mkStaticFieldSet (finfo:FieldInfo,value:Expr) =
if Unchecked.defaultof<FieldInfo> = finfo then raise (new ArgumentNullException())
checkTypesSR (typeOf value) finfo.FieldType "value" (SR.GetString(SR.QtmmBadFieldType))
match finfo.IsStatic with
| true -> mkFE1 (StaticFieldSetOp finfo) value
| false -> invalidArg "finfo" (SR.GetString(SR.QnonStaticNoReceiverObject))
let mkInstanceFieldSet (obj,finfo:FieldInfo,value:Expr) =
if Unchecked.defaultof<FieldInfo> = finfo then raise (new ArgumentNullException())
checkTypesSR (typeOf value) finfo.FieldType "value" (SR.GetString(SR.QtmmBadFieldType))
match finfo.IsStatic with
| false ->
checkObj finfo obj
mkFE2 (InstanceFieldSetOp finfo) (obj,value)
| true -> invalidArg "finfo" (SR.GetString(SR.QstaticWithReceiverObject))
let mkCtorCall (ci:ConstructorInfo,args:list<Expr>) =
if Unchecked.defaultof<ConstructorInfo> = ci then raise (new ArgumentNullException())
checkArgs (ci.GetParameters()) args
mkFEN (NewObjectOp ci) args
let mkDefaultValue (ty:Type) =
mkFE0 (DefaultValueOp ty)
let mkStaticPropGet (pinfo:PropertyInfo,args:list<Expr>) =
if Unchecked.defaultof<PropertyInfo> = pinfo then raise (new ArgumentNullException())
if (not pinfo.CanRead) then invalidArg "pinfo" (SR.GetString(SR.QreadingSetOnly))
checkArgs (pinfo.GetIndexParameters()) args
match pinfo.GetGetMethod(true).IsStatic with
| true -> mkFEN (StaticPropGetOp pinfo) args
| false -> invalidArg "pinfo" (SR.GetString(SR.QnonStaticNoReceiverObject))
let mkInstancePropGet (obj,pinfo:PropertyInfo,args:list<Expr>) =
if Unchecked.defaultof<PropertyInfo> = pinfo then raise (new ArgumentNullException())
if (not pinfo.CanRead) then invalidArg "pinfo" (SR.GetString(SR.QreadingSetOnly))
checkArgs (pinfo.GetIndexParameters()) args
match pinfo.GetGetMethod(true).IsStatic with
| false ->
checkObj pinfo obj
mkFEN (InstancePropGetOp pinfo) (obj::args)
| true -> invalidArg "pinfo" (SR.GetString(SR.QstaticWithReceiverObject))
let mkStaticPropSet (pinfo:PropertyInfo,args:list<Expr>,value:Expr) =
if Unchecked.defaultof<PropertyInfo> = pinfo then raise (new ArgumentNullException())
if (not pinfo.CanWrite) then invalidArg "pinfo" (SR.GetString(SR.QwritingGetOnly))
checkArgs (pinfo.GetIndexParameters()) args
match pinfo.GetSetMethod(true).IsStatic with
| true -> mkFEN (StaticPropSetOp pinfo) (args@[value])
| false -> invalidArg "pinfo" (SR.GetString(SR.QnonStaticNoReceiverObject))
let mkInstancePropSet (obj,pinfo:PropertyInfo,args:list<Expr>,value:Expr) =
if Unchecked.defaultof<PropertyInfo> = pinfo then raise (new ArgumentNullException())
if (not pinfo.CanWrite) then invalidArg "pinfo" (SR.GetString(SR.QwritingGetOnly))
checkArgs (pinfo.GetIndexParameters()) args
match pinfo.GetSetMethod(true).IsStatic with
| false ->
checkObj pinfo obj
mkFEN (InstancePropSetOp pinfo) (obj::(args@[value]))
| true -> invalidArg "pinfo" (SR.GetString(SR.QstaticWithReceiverObject))
let mkInstanceMethodCall (obj,minfo:MethodInfo,args:list<Expr>) =
if Unchecked.defaultof<MethodInfo> = minfo then raise (new ArgumentNullException())
checkArgs (minfo.GetParameters()) args
match minfo.IsStatic with
| false ->
checkObj minfo obj
mkFEN (InstanceMethodCallOp minfo) (obj::args)
| true -> invalidArg "minfo" (SR.GetString(SR.QstaticWithReceiverObject))
let mkStaticMethodCall (minfo:MethodInfo,args:list<Expr>) =
if Unchecked.defaultof<MethodInfo> = minfo then raise (new ArgumentNullException())
checkArgs (minfo.GetParameters()) args
match minfo.IsStatic with
| true -> mkFEN (StaticMethodCallOp minfo) args
| false -> invalidArg "minfo" (SR.GetString(SR.QnonStaticNoReceiverObject))
let mkForLoop (v:Var,lowerBound,upperBound,body) =
checkTypesSR (typeof<int>) (typeOf lowerBound) "lowerBound" (SR.GetString(SR.QtmmLowerUpperBoundMustBeInt))
checkTypesSR (typeof<int>) (typeOf upperBound) "upperBound" (SR.GetString(SR.QtmmLowerUpperBoundMustBeInt))
checkTypesSR (typeof<int>) (v.Type) "for" (SR.GetString(SR.QtmmLoopBodyMustBeLambdaTakingInteger))
mkFE3 ForIntegerRangeLoopOp (lowerBound, upperBound, mkLambda(v,body))
let mkWhileLoop (guard,body) =
checkTypesSR (typeof<bool>) (typeOf guard) "guard" (SR.GetString(SR.QtmmGuardMustBeBool))
checkTypesSR (typeof<Unit>) (typeOf body) "body" (SR.GetString(SR.QtmmBodyMustBeUnit))
mkFE2 (WhileLoopOp) (guard,body)
let mkNewDelegate (ty,e) =
let mi = getDelegateInvoke ty
let ps = mi.GetParameters()
let dlfun = Array.foldBack (fun (p:ParameterInfo) rty -> mkFunTy p.ParameterType rty) ps mi.ReturnType
checkTypesSR dlfun (typeOf e) "ty" (SR.GetString(SR.QtmmFunTypeNotMatchDelegate))
mkFE1 (NewDelegateOp ty) e
let mkLet (v,e,b) =
checkBind (v,e);
mkLetRaw (e,mkLambda(v,b))
//let mkLambdas(vs,b) = mkRLinear mkLambdaRaw (vs,(b:>Expr))
let mkTupledApplication (f,args) =
match args with
| [] -> mkApplication (f,mkUnit())
| [x] -> mkApplication (f,x)
| _ -> mkApplication (f,mkNewTuple args)
let mkApplications(f: Expr,es:list<list<Expr>>) = mkLLinear mkTupledApplication (f,es)
let mkIteratedLambdas(vs,b) = mkRLinear mkLambda (vs,b)
let mkLetRecRaw v = mkFE1 LetRecOp v
let mkLetRecCombRaw v = mkFEN LetRecCombOp v
let mkLetRec (ves:(Var*Expr) list,body) =
List.iter checkBind ves;
let vs,es = List.unzip ves
mkLetRecRaw(mkIteratedLambdas (vs,mkLetRecCombRaw (body::es)))
let ReflectedDefinitionsResourceNameBase = "ReflectedDefinitions"
//-------------------------------------------------------------------------
// General Method Binder
/// Usually functions in modules are not overloadable so having name is enough to recover the function.
/// However type extensions break this assumption - it is possible to have multiple extension methods in module that will have the same name.
/// This type is used to denote different binding results so we can distinguish the latter case and retry binding later when more information is available.
[<NoEquality; NoComparison>]
type ModuleDefinitionBindingResult<'T, 'R> =
| Unique of 'T
| Ambiguous of 'R
let typeEquals (s:Type) (t:Type) = s.Equals(t)
let typesEqual (ss:Type list) (tt:Type list) =
(ss.Length = tt.Length) && List.forall2 typeEquals ss tt
let instFormal (typarEnv: Type[]) (ty:Instantiable<'T>) = ty (fun i -> typarEnv.[i])
let getGenericArguments(tc:Type) =
if tc.IsGenericType then tc.GetGenericArguments() else [| |]
let getNumGenericArguments(tc:Type) =
if tc.IsGenericType then tc.GetGenericArguments().Length else 0
let bindMethodBySearch (parentT:Type,nm,marity,argtys,rty) =
let methInfos = parentT.GetMethods(staticOrInstanceBindingFlags) |> Array.toList
// First, filter on name, if unique, then binding "done"
let tyargTs = getGenericArguments(parentT)
let methInfos = methInfos |> List.filter (fun methInfo -> methInfo.Name = nm)
match methInfos with
| [methInfo] ->
methInfo
| _ ->
// Second, type match.
let select (methInfo:MethodInfo) =
// mref implied Types
let mtyargTIs = if methInfo.IsGenericMethod then methInfo.GetGenericArguments() else [| |]
if mtyargTIs.Length <> marity then false (* method generic arity mismatch *) else
let typarEnv = (Array.append tyargTs mtyargTIs)
let argTs = argtys |> List.map (instFormal typarEnv)
let resT = instFormal typarEnv rty
// methInfo implied Types
let haveArgTs =
let parameters = Array.toList (methInfo.GetParameters())
parameters |> List.map (fun param -> param.ParameterType)
let haveResT = methInfo.ReturnType
// check for match
if argTs.Length <> haveArgTs.Length then false (* method argument length mismatch *) else
let res = typesEqual (resT::argTs) (haveResT::haveArgTs)
res
// return MethodInfo for (generic) type's (generic) method
match List.tryFind select methInfos with
| None -> raise <| System.InvalidOperationException (SR.GetString SR.QcannotBindToMethod)
| Some methInfo -> methInfo
let bindMethodHelper (parentT: Type, nm,marity,argtys,rty) =
if isNull parentT then invalidArg "parentT" (SR.GetString(SR.QparentCannotBeNull))
if marity = 0 then
let tyargTs = if parentT.IsGenericType then parentT.GetGenericArguments() else [| |]
let argTs = Array.ofList (List.map (instFormal tyargTs) argtys)
let resT = instFormal tyargTs rty
let methInfo =
try
#if FX_RESHAPED_REFLECTION
match parentT.GetMethod(nm,argTs) with
#else
match parentT.GetMethod(nm,staticOrInstanceBindingFlags,null,argTs,null) with
#endif
| null -> None
| res -> Some(res)
with :? AmbiguousMatchException -> None
match methInfo with
| Some methInfo when (typeEquals resT methInfo.ReturnType) -> methInfo
| _ -> bindMethodBySearch(parentT,nm,marity,argtys,rty)
else
bindMethodBySearch(parentT,nm,marity,argtys,rty)
let bindModuleProperty (ty:Type,nm) =
match ty.GetProperty(nm,staticBindingFlags) with
| null -> raise <| System.InvalidOperationException (String.Format(SR.GetString(SR.QcannotBindProperty), nm, ty.ToString()))
| res -> res
// tries to locate unique function in a given type
// in case of multiple candidates returns None so bindModuleFunctionWithCallSiteArgs will be used for more precise resolution
let bindModuleFunction (ty:Type,nm) =
match ty.GetMethods(staticBindingFlags) |> Array.filter (fun mi -> mi.Name = nm) with
| [||] -> raise <| System.InvalidOperationException (String.Format(SR.GetString(SR.QcannotBindFunction), nm, ty.ToString()))
| [| res |] -> Some res
| _ -> None
let bindModuleFunctionWithCallSiteArgs (ty:Type, nm, argTypes : Type list, tyArgs : Type list) =
let argTypes = List.toArray argTypes
let tyArgs = List.toArray tyArgs
let methInfo =
try
#if FX_RESHAPED_REFLECTION
match ty.GetMethod(nm, argTypes) with
#else
match ty.GetMethod(nm,staticOrInstanceBindingFlags,null, argTypes,null) with
#endif
| null -> None
| res -> Some(res)
with :? AmbiguousMatchException -> None
match methInfo with
| Some methInfo -> methInfo
| _ ->
// narrow down set of candidates by removing methods with a different name\number of arguments\number of type parameters
let candidates =
ty.GetMethods(staticBindingFlags)
|> Array.filter(fun mi ->
mi.Name = nm &&
mi.GetParameters().Length = argTypes.Length &&
let methodTyArgCount = if mi.IsGenericMethod then mi.GetGenericArguments().Length else 0
methodTyArgCount = tyArgs.Length
)
let fail() = raise <| System.InvalidOperationException (String.Format(SR.GetString(SR.QcannotBindFunction), nm, ty.ToString()))
match candidates with
| [||] -> fail()
| [| solution |] -> solution
| candidates ->
let solution =
// no type arguments - just perform pairwise comparison of type in methods signature and argument type from the callsite
if tyArgs.Length = 0 then
candidates
|> Array.tryFind(fun mi ->
let paramTys = mi.GetParameters() |> Array.map (fun pi -> pi.ParameterType)
Array.forall2 (=) argTypes paramTys
)
else
let FAIL = -1
let MATCH = 2
let GENERIC_MATCH = 1
// if signature has type arguments then it is possible to have several candidates like
// - Foo(_ : 'a)
// - Foo(_ : int)
// and callsite
// - Foo<int>(_ : int)
// here instantiation of first method we'll have two similar signatures
// however compiler will pick second one and we must do the same.
// here we compute weights for every signature
// for every parameter type:
// - non-matching with actual argument type stops computation and return FAIL as the final result
// - exact match with actual argument type adds MATCH value to the final result
// - parameter type is generic that after instantiation matches actual argument type adds GENERIC_MATCH to the final result
// - parameter type is generic that after instantiation doesn't actual argument type stops computation and return FAIL as the final result
let weight (mi : MethodInfo) =
let parameters = mi.GetParameters()
let rec iter i acc =
if i >= argTypes.Length then acc
else
let param = parameters.[i]
if param.ParameterType.IsGenericParameter then
let actualTy = tyArgs.[param.ParameterType.GenericParameterPosition]
if actualTy = argTypes.[i] then iter (i + 1) (acc + GENERIC_MATCH) else FAIL
else
if param.ParameterType = argTypes.[i] then iter (i + 1) (acc + MATCH) else FAIL
iter 0 0
let solution, weight =
candidates
|> Array.map (fun mi -> mi, weight mi)
|> Array.maxBy snd
if weight = FAIL then None
else Some solution
match solution with
| Some mi -> mi
| None -> fail()
let mkNamedType (tc:Type,tyargs) =
match tyargs with
| [] -> tc
| _ -> tc.MakeGenericType(Array.ofList tyargs)
let inline checkNonNullResult (arg:string,err:string) y =
match box y with
| null -> raise (new ArgumentNullException(arg,err))
| _ -> y
let inst (tyargs:Type list) (i: Instantiable<'T>) = i (fun idx -> tyargs.[idx]) // Note, O(n) looks, but #tyargs is always small
let bindPropBySearchIfCandidateIsNull (ty : Type) propName retType argTypes candidate =
match candidate with
| null ->
let props =
ty.GetProperties(staticOrInstanceBindingFlags)
|> Array.filter (fun pi ->
let paramTypes = getTypesFromParamInfos (pi.GetIndexParameters())
pi.Name = propName &&
pi.PropertyType = retType &&
Array.length argTypes = paramTypes.Length &&
Array.forall2 (=) argTypes paramTypes
)
match props with
| [| pi |] -> pi
| _ -> null
| pi -> pi
let bindCtorBySearchIfCandidateIsNull (ty : Type) argTypes candidate =
match candidate with
| null ->
let ctors =
ty.GetConstructors(instanceBindingFlags)
|> Array.filter (fun ci ->
let paramTypes = getTypesFromParamInfos (ci.GetParameters())
Array.length argTypes = paramTypes.Length &&
Array.forall2 (=) argTypes paramTypes
)
match ctors with
| [| ctor |] -> ctor
| _ -> null
| ctor -> ctor
let bindProp (tc,propName,retType,argTypes,tyargs) =
// We search in the instantiated type, rather than searching the generic type.
let typ = mkNamedType(tc,tyargs)
let argtyps : Type list = argTypes |> inst tyargs
let retType : Type = retType |> inst tyargs |> removeVoid
#if FX_RESHAPED_REFLECTION
try
typ.GetProperty(propName, staticOrInstanceBindingFlags)
with :? AmbiguousMatchException -> null // more than one property found with the specified name and matching binding constraints - return null to initiate manual search
|> bindPropBySearchIfCandidateIsNull typ propName retType (Array.ofList argtyps)
|> checkNonNullResult ("propName", String.Format(SR.GetString(SR.QfailedToBindProperty), propName)) // fxcop may not see "propName" as an arg
#else
typ.GetProperty(propName, staticOrInstanceBindingFlags, null, retType, Array.ofList argtyps, null) |> checkNonNullResult ("propName", String.Format(SR.GetString(SR.QfailedToBindProperty), propName)) // fxcop may not see "propName" as an arg
#endif
let bindField (tc,fldName,tyargs) =
let typ = mkNamedType(tc,tyargs)
typ.GetField(fldName,staticOrInstanceBindingFlags) |> checkNonNullResult ("fldName", String.Format(SR.GetString(SR.QfailedToBindField), fldName)) // fxcop may not see "fldName" as an arg
let bindGenericCctor (tc:Type) =
tc.GetConstructor(staticBindingFlags,null,[| |],null)
|> checkNonNullResult ("tc", SR.GetString(SR.QfailedToBindConstructor))
let bindGenericCtor (tc:Type,argTypes:Instantiable<Type list>) =
let argtyps = instFormal (getGenericArguments tc) argTypes
#if FX_RESHAPED_REFLECTION
let argTypes = Array.ofList argtyps
tc.GetConstructor(argTypes)
|> bindCtorBySearchIfCandidateIsNull tc argTypes
|> checkNonNullResult ("tc", SR.GetString(SR.QfailedToBindConstructor))
#else
tc.GetConstructor(instanceBindingFlags,null,Array.ofList argtyps,null) |> checkNonNullResult ("tc", SR.GetString(SR.QfailedToBindConstructor))
#endif
let bindCtor (tc,argTypes:Instantiable<Type list>,tyargs) =
let typ = mkNamedType(tc,tyargs)
let argtyps = argTypes |> inst tyargs
#if FX_RESHAPED_REFLECTION
let argTypes = Array.ofList argtyps
typ.GetConstructor(argTypes)
|> bindCtorBySearchIfCandidateIsNull typ argTypes
|> checkNonNullResult ("tc", SR.GetString(SR.QfailedToBindConstructor))
#else
typ.GetConstructor(instanceBindingFlags,null,Array.ofList argtyps,null) |> checkNonNullResult ("tc", SR.GetString(SR.QfailedToBindConstructor))
#endif
let chop n xs =
if n < 0 then invalidArg "n" (SR.GetString(SR.inputMustBeNonNegative))
let rec split l =
match l with
| 0,xs -> [],xs
| n,x::xs -> let front,back = split (n-1,xs)
x::front,back
| _,[] -> failwith "List.chop: not enough elts list"
split (n,xs)
let instMeth (ngmeth: MethodInfo, methTypeArgs) =
if ngmeth.GetGenericArguments().Length = 0 then ngmeth(* non generic *)
else ngmeth.MakeGenericMethod(Array.ofList methTypeArgs)
let bindGenericMeth (tc:Type,argTypes : list<Instantiable<Type>>,retType,methName,numMethTyargs) =
bindMethodHelper(tc,methName,numMethTyargs,argTypes,retType)
let bindMeth ((tc:Type,argTypes : list<Instantiable<Type>>,retType,methName,numMethTyargs),tyargs) =
let ntyargs = tc.GetGenericArguments().Length
let enclTypeArgs,methTypeArgs = chop ntyargs tyargs
let ty = mkNamedType(tc,enclTypeArgs)
let ngmeth = bindMethodHelper(ty,methName,numMethTyargs,argTypes,retType)
instMeth(ngmeth,methTypeArgs)
let pinfoIsStatic (pinfo:PropertyInfo) =
if pinfo.CanRead then pinfo.GetGetMethod(true).IsStatic
elif pinfo.CanWrite then pinfo.GetSetMethod(true).IsStatic
else false
//--------------------------------------------------------------------------
// Unpickling
//--------------------------------------------------------------------------
module SimpleUnpickle =
[<NoEquality; NoComparison>]
type InputState =
{ is: ByteStream;
istrings: string[];
localAssembly: System.Reflection.Assembly
referencedTypeDefs: Type[] }
let u_byte_as_int st = st.is.ReadByte()
let u_bool st = let b = u_byte_as_int st in (b = 1)
let u_void (_: InputState) = ()
let u_unit (_: InputState) = ()
let prim_u_int32 st =
let b0 = (u_byte_as_int st)
let b1 = (u_byte_as_int st)
let b2 = (u_byte_as_int st)
let b3 = (u_byte_as_int st)
b0 ||| (b1 <<< 8) ||| (b2 <<< 16) ||| (b3 <<< 24)
let u_int32 st =
let b0 = u_byte_as_int st
if b0 <= 0x7F then b0
elif b0 <= 0xbf then
let b0 = b0 &&& 0x7f
let b1 = (u_byte_as_int st)
(b0 <<< 8) ||| b1
else
prim_u_int32 st
let u_bytes st =
let n = u_int32 st
st.is.ReadBytes(n)
let prim_u_string st =
let len = (u_int32 st)
st.is.ReadUtf8BytesAsString(len)
let u_int st = u_int32 st
let u_sbyte st = sbyte (u_int32 st)
let u_byte st = byte (u_byte_as_int st)
let u_int16 st = int16 (u_int32 st)
let u_uint16 st = uint16 (u_int32 st)
let u_uint32 st = uint32 (u_int32 st)
let u_int64 st =
let b1 = int64 (u_int32 st) &&& 0xFFFFFFFFL
let b2 = int64 (u_int32 st)
b1 ||| (b2 <<< 32)
let u_uint64 st = uint64 (u_int64 st)
let u_double st = System.BitConverter.ToDouble(System.BitConverter.GetBytes(u_int64 st),0)
let u_float32 st = System.BitConverter.ToSingle(System.BitConverter.GetBytes(u_int32 st),0)
let u_char st = char (int32 (u_uint16 st))
let inline u_tup2 p1 p2 st = let a = p1 st in let b = p2 st in (a,b)
let inline u_tup3 p1 p2 p3 st =
let a = p1 st in let b = p2 st in let c = p3 st in (a,b,c)
let inline u_tup4 p1 p2 p3 p4 st =
let a = p1 st in let b = p2 st in let c = p3 st in let d = p4 st in (a,b,c,d)
let inline u_tup5 p1 p2 p3 p4 p5 st =
let a = p1 st in let b = p2 st in let c = p3 st in let d = p4 st in let e = p5 st in (a,b,c,d,e)
let u_uniq (tbl: _ array) st =
let n = u_int st
if n < 0 || n >= tbl.Length then failwith ("u_uniq: out of range, n = "+string n+ ", sizeof(tab) = " + string tbl.Length);
tbl.[n]
let u_string st = u_uniq st.istrings st
let rec u_list_aux f acc st =
let tag = u_byte_as_int st
match tag with
| 0 -> List.rev acc
| 1 -> let a = f st in u_list_aux f (a::acc) st
| n -> failwith ("u_list: found number " + string n)
let u_list f st = u_list_aux f [] st
let unpickleObj localAssembly referencedTypeDefs u phase2bytes =
let phase2data =
let st2 =
{ is = new ByteStream(phase2bytes,0,phase2bytes.Length)
istrings = [| |]
localAssembly=localAssembly
referencedTypeDefs=referencedTypeDefs }
u_tup2 (u_list prim_u_string) u_bytes st2
let stringTab,phase1bytes = phase2data
let st1 =
{ is = new ByteStream(phase1bytes,0,phase1bytes.Length)
istrings = Array.ofList stringTab
localAssembly=localAssembly
referencedTypeDefs=referencedTypeDefs }
let res = u st1
res
open SimpleUnpickle
let decodeFunTy args =
match args with
| [d;r] -> funTyC.MakeGenericType([| d; r |])
| _ -> invalidArg "args" (SR.GetString(SR.QexpectedTwoTypes))
let decodeArrayTy n (tys: Type list) =
match tys with
| [ty] -> if (n = 1) then ty.MakeArrayType() else ty.MakeArrayType(n)
// typeof<int>.MakeArrayType(1) returns "Int[*]" but we need "Int[]"
| _ -> invalidArg "tys" (SR.GetString(SR.QexpectedOneType))
let mkNamedTycon (tcName,ass:Assembly) =
match ass.GetType(tcName) with
| null ->
// For some reason we can get 'null' returned here even when a type with the right name exists... Hence search the slow way...
match (ass.GetTypes() |> Array.tryFind (fun a -> a.FullName = tcName)) with
| Some ty -> ty
| None -> invalidArg "tcName" (String.Format(SR.GetString(SR.QfailedToBindTypeInAssembly), tcName, ass.FullName)) // "Available types are:\n%A" tcName ass (ass.GetTypes() |> Array.map (fun a -> a.FullName))
| ty -> ty
let decodeNamedTy tc tsR = mkNamedType(tc,tsR)
let mscorlib = typeof<System.Int32>.Assembly
let u_assref st = u_string st
let decodeAssemblyRef st a =
if a = "" then mscorlib
elif a = "." then st.localAssembly
else
#if FX_RESHAPED_REFLECTION
match System.Reflection.Assembly.Load(AssemblyName(a)) with
#else
match System.Reflection.Assembly.Load(a) with
#endif
| null -> raise <| System.InvalidOperationException(String.Format(SR.GetString(SR.QfailedToBindAssembly), a.ToString()))
| ass -> ass
let u_NamedType st =
let a,b = u_tup2 u_string u_assref st
let mutable idx = 0
// From FSharp.Core for F# 4.0+ (4.4.0.0+), referenced type definitions can be integer indexes into a table of type definitions provided on quotation
// deserialization, avoiding the need for System.Reflection.Assembly.Load
if System.Int32.TryParse(a, &idx) && b = "" then
st.referencedTypeDefs.[idx]
else
// escape commas found in type name, which are not already escaped
// '\' is not valid in a type name except as an escape character, so logic can be pretty simple
let escapedTcName = System.Text.RegularExpressions.Regex.Replace(a, @"(?<!\\),", @"\,")
let assref = decodeAssemblyRef st b
mkNamedTycon (escapedTcName, assref)
let u_tyconstSpec st =
let tag = u_byte_as_int st
match tag with
| 1 -> u_unit st |> (fun () -> decodeFunTy)
| 2 -> u_NamedType st |> decodeNamedTy
| 3 -> u_int st |> decodeArrayTy
| _ -> failwith "u_tyconstSpec"
let appL fs env = List.map (fun f -> f env) fs
let rec u_dtype st : (int -> Type) -> Type =
let tag = u_byte_as_int st
match tag with
| 0 -> u_int st |> (fun x env -> env(x))
| 1 -> u_tup2 u_tyconstSpec (u_list u_dtype) st |> (fun (a,b) env -> a (appL b env))
| _ -> failwith "u_dtype"
let u_dtypes st = let a = u_list u_dtype st in appL a
let (|NoTyArgs|)input = match input with [] -> () | _ -> failwith "incorrect number of arguments during deserialization"
let (|OneTyArg|)input = match input with [x] -> x | _ -> failwith "incorrect number of arguments during deserialization"
[<NoEquality; NoComparison>]
type BindingEnv =
{ /// Mapping from variable index to Var object for the variable
vars : Map<int,Var>
/// The number of indexes in the mapping
varn: int
/// The active type instantiation for generic type parameters
typeInst : int -> Type }
let addVar env v =
{ env with vars = env.vars.Add(env.varn,v); varn=env.varn+1 }
let mkTyparSubst (tyargs:Type[]) =
let n = tyargs.Length
fun idx ->
if idx < n then tyargs.[idx]
else raise <| System.InvalidOperationException (SR.GetString(SR.QtypeArgumentOutOfRange))
let envClosed (spliceTypes:Type[]) =
{ vars = Map.empty;
varn = 0
typeInst = mkTyparSubst spliceTypes }
type Bindable<'T> = BindingEnv -> 'T
let rec u_Expr st =
let tag = u_byte_as_int st
match tag with
| 0 -> u_tup3 u_constSpec u_dtypes (u_list u_Expr) st
|> (fun (a,b,args) (env:BindingEnv) ->
let args = List.map (fun e -> e env) args
let a =
match a with
| Unique v -> v
| Ambiguous f ->
let argTys = List.map typeOf args
f argTys
let tyargs = b env.typeInst
E(CombTerm(a tyargs, args )))
| 1 -> let x = u_VarRef st
(fun env -> E(VarTerm (x env)))
| 2 -> let a = u_VarDecl st
let b = u_Expr st
(fun env -> let v = a env in E(LambdaTerm(v,b (addVar env v))))
| 3 -> let a = u_dtype st
let idx = u_int st
(fun env -> E(HoleTerm(a env.typeInst , idx)))
| 4 -> let a = u_Expr st
(fun env -> mkQuote(a env, true))
| 5 -> let a = u_Expr st
let attrs = u_list u_Expr st
(fun env -> let e = (a env) in EA(e.Tree,(e.CustomAttributes @ List.map (fun attrf -> attrf env) attrs)))
| 6 -> let a = u_dtype st
(fun env -> mkVar(Var.Global("this", a env.typeInst)))
| 7 -> let a = u_Expr st
(fun env -> mkQuote(a env, false))
| _ -> failwith "u_Expr"
and u_VarDecl st =
let s,b,mut = u_tup3 u_string u_dtype u_bool st
(fun env -> new Var(s, b env.typeInst, mut))
and u_VarRef st =
let i = u_int st
(fun env -> env.vars.[i])
and u_RecdField st =
let ty,nm = u_tup2 u_NamedType u_string st
(fun tyargs -> getRecordProperty(mkNamedType(ty,tyargs),nm))
and u_UnionCaseInfo st =
let ty,nm = u_tup2 u_NamedType u_string st
(fun tyargs -> getUnionCaseInfo(mkNamedType(ty,tyargs),nm))
and u_UnionCaseField st =
let case,i = u_tup2 u_UnionCaseInfo u_int st
(fun tyargs -> getUnionCaseInfoField(case tyargs,i))
and u_ModuleDefn st =
let (ty,nm,isProp) = u_tup3 u_NamedType u_string u_bool st
if isProp then Unique(StaticPropGetOp(bindModuleProperty(ty,nm)))
else
match bindModuleFunction(ty, nm) with
| Some mi -> Unique(StaticMethodCallOp(mi))
| None -> Ambiguous(fun argTypes tyargs -> StaticMethodCallOp(bindModuleFunctionWithCallSiteArgs(ty, nm, argTypes, tyargs)))
and u_MethodInfoData st =
u_tup5 u_NamedType (u_list u_dtype) u_dtype u_string u_int st
and u_PropInfoData st =
u_tup4 u_NamedType u_string u_dtype u_dtypes st
and u_CtorInfoData st =
u_tup2 u_NamedType u_dtypes st
and u_MethodBase st =
let tag = u_byte_as_int st
match tag with
| 0 ->
match u_ModuleDefn st with
| Unique(StaticMethodCallOp(minfo)) -> (minfo :> MethodBase)
| Unique(StaticPropGetOp(pinfo)) -> (pinfo.GetGetMethod(true) :> MethodBase)
| Ambiguous(_) -> raise (System.Reflection.AmbiguousMatchException())
| _ -> failwith "unreachable"
| 1 ->
let ((tc,_,_,methName,_) as data) = u_MethodInfoData st
if methName = ".cctor" then
let cinfo = bindGenericCctor tc
(cinfo :> MethodBase)
else
let minfo = bindGenericMeth(data)
(minfo :> MethodBase)
| 2 ->
let data = u_CtorInfoData st
let cinfo = bindGenericCtor(data) in
(cinfo :> MethodBase)
| _ -> failwith "u_MethodBase"
and u_constSpec st =
let tag = u_byte_as_int st
if tag = 1 then
let bindModuleDefn r tyargs =
match r with
| StaticMethodCallOp(minfo) -> StaticMethodCallOp(instMeth(minfo,tyargs))
// OK to throw away the tyargs here since this only non-generic values in modules get represented by static properties
| x -> x
match u_ModuleDefn st with
| Unique(r) -> Unique(bindModuleDefn r)
| Ambiguous(f) -> Ambiguous(fun argTypes tyargs -> bindModuleDefn (f argTypes tyargs) tyargs)
else
let constSpec =
match tag with
| 0 -> u_void st |> (fun () NoTyArgs -> IfThenElseOp)
| 2 -> u_void st |> (fun () NoTyArgs -> LetRecOp)
| 3 -> u_NamedType st |> (fun x tyargs -> NewRecordOp (mkNamedType(x,tyargs)))
| 4 -> u_RecdField st |> (fun prop tyargs -> InstancePropGetOp(prop tyargs))
| 5 -> u_UnionCaseInfo st |> (fun unionCase tyargs -> NewUnionCaseOp(unionCase tyargs))
| 6 -> u_UnionCaseField st |> (fun prop tyargs -> InstancePropGetOp(prop tyargs) )
| 7 -> u_UnionCaseInfo st |> (fun unionCase tyargs -> UnionCaseTestOp(unionCase tyargs))
| 8 -> u_void st |> (fun () (OneTyArg(tyarg)) -> NewTupleOp tyarg)
| 9 -> u_int st |> (fun x (OneTyArg(tyarg)) -> TupleGetOp (tyarg,x))
// Note, these get type args because they may be the result of reading literal field constants
| 11 -> u_bool st |> (fun x (OneTyArg(tyarg)) -> mkLiftedValueOpG (x, tyarg))
| 12 -> u_string st |> (fun x (OneTyArg(tyarg)) -> mkLiftedValueOpG (x, tyarg))
| 13 -> u_float32 st |> (fun x (OneTyArg(tyarg)) -> mkLiftedValueOpG (x, tyarg))
| 14 -> u_double st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 15 -> u_char st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 16 -> u_sbyte st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 17 -> u_byte st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 18 -> u_int16 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 19 -> u_uint16 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 20 -> u_int32 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 21 -> u_uint32 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 22 -> u_int64 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 23 -> u_uint64 st |> (fun a (OneTyArg(tyarg)) -> mkLiftedValueOpG (a, tyarg))
| 24 -> u_void st |> (fun () NoTyArgs -> mkLiftedValueOpG ((), typeof<unit>))
| 25 -> u_PropInfoData st |> (fun (a,b,c,d) tyargs -> let pinfo = bindProp(a,b,c,d,tyargs) in if pinfoIsStatic pinfo then StaticPropGetOp(pinfo) else InstancePropGetOp(pinfo))
| 26 -> u_CtorInfoData st |> (fun (a,b) tyargs -> NewObjectOp (bindCtor(a,b,tyargs)))
| 28 -> u_void st |> (fun () (OneTyArg(ty)) -> CoerceOp ty)
| 29 -> u_void st |> (fun () NoTyArgs -> SequentialOp)
| 30 -> u_void st |> (fun () NoTyArgs -> ForIntegerRangeLoopOp)
| 31 -> u_MethodInfoData st |> (fun p tyargs -> let minfo = bindMeth(p,tyargs) in if minfo.IsStatic then StaticMethodCallOp(minfo) else InstanceMethodCallOp(minfo))
| 32 -> u_void st |> (fun () (OneTyArg(ty)) -> NewArrayOp ty)
| 33 -> u_void st |> (fun () (OneTyArg(ty)) -> NewDelegateOp ty)
| 34 -> u_void st |> (fun () NoTyArgs -> WhileLoopOp)
| 35 -> u_void st |> (fun () NoTyArgs -> LetOp)
| 36 -> u_RecdField st |> (fun prop tyargs -> InstancePropSetOp(prop tyargs))
| 37 -> u_tup2 u_NamedType u_string st |> (fun (a,b) tyargs -> let finfo = bindField(a,b,tyargs) in if finfo.IsStatic then StaticFieldGetOp(finfo) else InstanceFieldGetOp(finfo))
| 38 -> u_void st |> (fun () NoTyArgs -> LetRecCombOp)
| 39 -> u_void st |> (fun () NoTyArgs -> AppOp)
| 40 -> u_void st |> (fun () (OneTyArg(ty)) -> ValueOp(null,ty,None))
| 41 -> u_void st |> (fun () (OneTyArg(ty)) -> DefaultValueOp(ty))
| 42 -> u_PropInfoData st |> (fun (a,b,c,d) tyargs -> let pinfo = bindProp(a,b,c,d,tyargs) in if pinfoIsStatic pinfo then StaticPropSetOp(pinfo) else InstancePropSetOp(pinfo))
| 43 -> u_tup2 u_NamedType u_string st |> (fun (a,b) tyargs -> let finfo = bindField(a,b,tyargs) in if finfo.IsStatic then StaticFieldSetOp(finfo) else InstanceFieldSetOp(finfo))
| 44 -> u_void st |> (fun () NoTyArgs -> AddressOfOp)
| 45 -> u_void st |> (fun () NoTyArgs -> AddressSetOp)
| 46 -> u_void st |> (fun () (OneTyArg(ty)) -> TypeTestOp(ty))
| 47 -> u_void st |> (fun () NoTyArgs -> TryFinallyOp)
| 48 -> u_void st |> (fun () NoTyArgs -> TryWithOp)
| 49 -> u_void st |> (fun () NoTyArgs -> VarSetOp)
| _ -> failwithf "u_constSpec, unrecognized tag %d" tag
Unique constSpec
let u_ReflectedDefinition = u_tup2 u_MethodBase u_Expr
let u_ReflectedDefinitions = u_list u_ReflectedDefinition
let unpickleExpr (localType: Type) referencedTypes bytes =
unpickleObj localType.Assembly referencedTypes u_Expr bytes
let unpickleReflectedDefns localAssembly referencedTypes bytes =
unpickleObj localAssembly referencedTypes u_ReflectedDefinitions bytes
//--------------------------------------------------------------------------
// General utilities that will eventually be folded into
// Microsoft.FSharp.Quotations.Typed
//--------------------------------------------------------------------------
/// Fill the holes in an Expr
let rec fillHolesInRawExpr (l:Expr[]) (E t as e) =
match t with
| VarTerm _ -> e
| LambdaTerm (v,b) -> EA(LambdaTerm(v, fillHolesInRawExpr l b ),e.CustomAttributes)
| CombTerm (op,args) -> EA(CombTerm(op, args |> List.map (fillHolesInRawExpr l)),e.CustomAttributes)
| HoleTerm (ty,idx) ->
if idx < 0 || idx >= l.Length then failwith "hole index out of range";
let h = l.[idx]
match typeOf h with
| expected when expected <> ty -> invalidArg "receivedType" (String.Format(SR.GetString(SR.QtmmRaw), expected, ty))
| _ -> h
let rec freeInExprAcc bvs acc (E t) =
match t with
| HoleTerm _ -> acc
| CombTerm (_, ag) -> ag |> List.fold (freeInExprAcc bvs) acc
| VarTerm v -> if Set.contains v bvs || Set.contains v acc then acc else Set.add v acc
| LambdaTerm (v,b) -> freeInExprAcc (Set.add v bvs) acc b
and freeInExpr e = freeInExprAcc Set.empty Set.empty e
// utility for folding
let foldWhile f st (ie: seq<'T>) =
use e = ie.GetEnumerator()
let mutable res = Some st
while (res.IsSome && e.MoveNext()) do
res <- f (match res with Some a -> a | _ -> failwith "internal error") e.Current;
res
[<NoEquality; NoComparison>]
exception Clash of Var
/// Replace type variables and expression variables with parameters using the
/// given substitution functions/maps.
let rec substituteInExpr bvs tmsubst (E t as e) =
match t with
| CombTerm (c, args) ->
let substargs = args |> List.map (fun arg -> substituteInExpr bvs tmsubst arg)
EA(CombTerm(c, substargs),e.CustomAttributes)
| VarTerm v ->
match tmsubst v with
| None -> e
| Some e2 ->
let fvs = freeInExpr e2
let clashes = Set.intersect fvs bvs in
if clashes.IsEmpty then e2
else raise (Clash(clashes.MinimumElement))
| LambdaTerm (v,b) ->
try EA(LambdaTerm(v,substituteInExpr (Set.add v bvs) tmsubst b),e.CustomAttributes)
with Clash(bv) ->
if v = bv then
let v2 = new Var(v.Name,v.Type)
let v2exp = E(VarTerm(v2))
EA(LambdaTerm(v2,substituteInExpr bvs (fun v -> if v = bv then Some(v2exp) else tmsubst v) b),e.CustomAttributes)
else
reraise()
| HoleTerm _ -> e
let substituteRaw tmsubst e = substituteInExpr Set.empty tmsubst e
let readToEnd (s : Stream) =
let n = int s.Length
let res = Array.zeroCreate n
let i = ref 0
while (!i < n) do
i := !i + s.Read(res,!i,(n - !i))
done;
res
let decodedTopResources = new Dictionary<Assembly * string, int>(10,HashIdentity.Structural)
#if !FX_NO_REFLECTION_METADATA_TOKENS
#if FX_NO_REFLECTION_MODULE_HANDLES // not available on Silverlight
[<StructuralEquality;StructuralComparison>]
type ModuleHandle = ModuleHandle of string * string
type System.Reflection.Module with
member x.ModuleHandle = ModuleHandle(x.Assembly.FullName, x.Name)
#else
type ModuleHandle = System.ModuleHandle
#endif
#endif
#if FX_NO_REFLECTION_METADATA_TOKENS // not available on Compact Framework
[<StructuralEquality; NoComparison>]
type ReflectedDefinitionTableKey =
// Key is declaring type * type parameters count * name * parameter types * return type
// Registered reflected definitions can contain generic methods or constructors in generic types,
// however TryGetReflectedDefinition can be queried with concrete instantiations of the same methods that doesn't contain type parameters.
// To make these two cases match we apply the following transformations:
// 1. if declaring type is generic - key will contain generic type definition, otherwise - type itself
// 2. if method is instantiation of generic one - pick parameters from generic method definition, otherwise - from methods itself
// 3 if method is constructor and declaring type is generic then we'll use the following trick to treat C<'a>() and C<int>() as the same type
// - we resolve method handle of the constructor using generic type definition - as a result for constructor from instantiated type we obtain matching constructor in generic type definition
| Key of System.Type * int * string * System.Type[] * System.Type
static member GetKey(methodBase:MethodBase) =
let isGenericType = methodBase.DeclaringType.IsGenericType
let declaringType =
if isGenericType then
methodBase.DeclaringType.GetGenericTypeDefinition()
else methodBase.DeclaringType
let tyArgsCount =
if methodBase.IsGenericMethod then
methodBase.GetGenericArguments().Length
else 0
#if FX_RESHAPED_REFLECTION
// this is very unfortunate consequence of limited Reflection capabilities on .NETCore
// what we want: having MethodBase for some concrete method or constructor we would like to locate corresponding MethodInfo\ConstructorInfo from the open generic type (canonical form).
// It is necessary to build the key for the table of reflected definitions: reflection definition is saved for open generic type but user may request it using
// arbitrary instantiation.
let findMethodInOpenGenericType (mb : ('T :> MethodBase)) : 'T =
let candidates =
let bindingFlags =
(if mb.IsPublic then BindingFlags.Public else BindingFlags.NonPublic) |||
(if mb.IsStatic then BindingFlags.Static else BindingFlags.Instance)
let candidates : MethodBase[] =
downcast (
if mb.IsConstructor then
box (declaringType.GetConstructors(bindingFlags))
else
box (declaringType.GetMethods(bindingFlags))
)
candidates |> Array.filter (fun c ->
c.Name = mb.Name &&
(c.GetParameters().Length) = (mb.GetParameters().Length) &&
(c.IsGenericMethod = mb.IsGenericMethod) &&
(if c.IsGenericMethod then c.GetGenericArguments().Length = mb.GetGenericArguments().Length else true)
)
let solution =
if candidates.Length = 0 then failwith "Unexpected, failed to locate matching method"
elif candidates.Length = 1 then candidates.[0]
else
// here we definitely know that candidates
// a. has matching name
// b. has the same number of arguments
// c. has the same number of type parameters if any
let originalParameters = mb.GetParameters()
let originalTypeArguments = mb.DeclaringType.GetGenericArguments()
let EXACT_MATCHING_COST = 2
let GENERIC_TYPE_MATCHING_COST = 1
// loops through the parameters and computes the rate of the current candidate.
// having the argument:
// - rate is increased on EXACT_MATCHING_COST if type of argument that candidate has at position i exactly matched the type of argument for the original method.
// - rate is increased on GENERIC_TYPE_MATCHING_COST if candidate has generic argument at given position and its type matched the type of argument for the original method.
// - otherwise rate will be 0
let evaluateCandidate (mb : MethodBase) : int =
let parameters = mb.GetParameters()
let rec loop i resultSoFar =
if i >= parameters.Length then resultSoFar
else
let p = parameters.[i]
let orig = originalParameters.[i]
if p.ParameterType = orig.ParameterType then loop (i + 1) (resultSoFar + EXACT_MATCHING_COST) // exact matching
elif p.ParameterType.IsGenericParameter && p.ParameterType.DeclaringType = mb.DeclaringType then
let pos = p.ParameterType.GenericParameterPosition
if originalTypeArguments.[pos] = orig.ParameterType then loop (i + 1) (resultSoFar + GENERIC_TYPE_MATCHING_COST)
else 0
else
0
loop 0 0
Array.maxBy evaluateCandidate candidates
solution :?> 'T
#endif
match methodBase with
| :? MethodInfo as mi ->
let mi =
if mi.IsGenericMethod then
let mi = mi.GetGenericMethodDefinition()
if isGenericType then
#if FX_RESHAPED_REFLECTION
findMethodInOpenGenericType mi
#else
MethodBase.GetMethodFromHandle(mi.MethodHandle, declaringType.TypeHandle) :?> MethodInfo
#endif
else
mi
else mi
let paramTypes = mi.GetParameters() |> getTypesFromParamInfos
Key(declaringType, tyArgsCount, methodBase.Name, paramTypes, mi.ReturnType)
| :? ConstructorInfo as ci ->
let mi =
if isGenericType then
#if FX_RESHAPED_REFLECTION
findMethodInOpenGenericType ci
#else
MethodBase.GetMethodFromHandle(ci. MethodHandle, declaringType.TypeHandle) :?> ConstructorInfo // convert ctor with concrete args to ctor with generic args
#endif
else
ci
let paramTypes = mi.GetParameters() |> getTypesFromParamInfos
Key(declaringType, tyArgsCount, methodBase.Name, paramTypes, declaringType)
| _ -> failwithf "Unexpected MethodBase type, %A" (methodBase.GetType()) // per MSDN ConstructorInfo and MethodInfo are the only derived types from MethodBase
#else
[<StructuralEquality; NoComparison>]
type ReflectedDefinitionTableKey =
| Key of ModuleHandle * int
static member GetKey(methodBase:MethodBase) =
Key(methodBase.Module.ModuleHandle,methodBase.MetadataToken)
#endif
[<NoEquality; NoComparison>]
type ReflectedDefinitionTableEntry = Entry of Bindable<Expr>
let reflectedDefinitionTable = new Dictionary<ReflectedDefinitionTableKey,ReflectedDefinitionTableEntry>(10,HashIdentity.Structural)
let registerReflectedDefinitions (assem, resourceName, bytes, referencedTypes) =
let defns = unpickleReflectedDefns assem referencedTypes bytes
defns |> List.iter (fun (minfo,exprBuilder) ->
let key = ReflectedDefinitionTableKey.GetKey minfo
lock reflectedDefinitionTable (fun () ->
reflectedDefinitionTable.Add(key,Entry(exprBuilder))))
decodedTopResources.Add((assem,resourceName),0)
let tryGetReflectedDefinition (methodBase: MethodBase, tyargs: Type []) =
checkNonNull "methodBase" methodBase
let data =
let assem = methodBase.DeclaringType.Assembly
let key = ReflectedDefinitionTableKey.GetKey methodBase
let ok,res = lock reflectedDefinitionTable (fun () -> reflectedDefinitionTable.TryGetValue(key))
if ok then Some res else
let qdataResources =
// dynamic assemblies don't support the GetManifestResourceNames
match assem with
| a when a.FullName = "System.Reflection.Emit.AssemblyBuilder" -> []
| null | _ ->
let resources =
// This raises NotSupportedException for dynamic assemblies
try assem.GetManifestResourceNames()
with :? NotSupportedException -> [| |]
[ for resourceName in resources do
if resourceName.StartsWith(ReflectedDefinitionsResourceNameBase,StringComparison.Ordinal) &&
not (decodedTopResources.ContainsKey((assem,resourceName))) then
let cmaAttribForResource =
#if FX_RESHAPED_REFLECTION
CustomAttributeExtensions.GetCustomAttributes(assem, typeof<CompilationMappingAttribute>) |> Seq.toArray
#else
assem.GetCustomAttributes(typeof<CompilationMappingAttribute>, false)
#endif
|> (function null -> [| |] | x -> x)
|> Array.tryPick (fun ca ->
match ca with
| :? CompilationMappingAttribute as cma when cma.ResourceName = resourceName -> Some cma
| _ -> None)
let resourceBytes = readToEnd (assem.GetManifestResourceStream(resourceName))
let referencedTypes =
match cmaAttribForResource with
| None -> [| |]
| Some cma -> cma.TypeDefinitions
yield (resourceName,unpickleReflectedDefns assem referencedTypes resourceBytes) ]
// ok, add to the table
let ok,res =
lock reflectedDefinitionTable (fun () ->
// check another thread didn't get in first
if not (reflectedDefinitionTable.ContainsKey(key)) then
qdataResources
|> List.iter (fun (resourceName,defns) ->
defns |> List.iter (fun (methodBase,exprBuilder) ->
reflectedDefinitionTable.[ReflectedDefinitionTableKey.GetKey methodBase] <- Entry(exprBuilder));
decodedTopResources.Add((assem,resourceName),0))
// we know it's in the table now, if it's ever going to be there
reflectedDefinitionTable.TryGetValue(key)
);
if ok then Some res else None
match data with
| Some (Entry(exprBuilder)) ->
let expectedNumTypars =
getNumGenericArguments(methodBase.DeclaringType) +
(match methodBase with
| :? MethodInfo as minfo -> if minfo.IsGenericMethod then minfo.GetGenericArguments().Length else 0
| _ -> 0)
if (expectedNumTypars <> tyargs.Length) then
invalidArg "tyargs" (String.Format(SR.GetString(SR.QwrongNumOfTypeArgs), methodBase.Name, expectedNumTypars.ToString(), tyargs.Length.ToString()));
Some(exprBuilder (envClosed tyargs))
| None -> None
let tryGetReflectedDefinitionInstantiated (methodBase:MethodBase) =
checkNonNull "methodBase" methodBase
match methodBase with
| :? MethodInfo as minfo ->
let tyargs =
Array.append
(getGenericArguments minfo.DeclaringType)
(if minfo.IsGenericMethod then minfo.GetGenericArguments() else [| |])
tryGetReflectedDefinition (methodBase, tyargs)
| :? ConstructorInfo as cinfo ->
let tyargs = getGenericArguments cinfo.DeclaringType
tryGetReflectedDefinition (methodBase, tyargs)
| _ ->
tryGetReflectedDefinition (methodBase, [| |])
let deserialize (localAssembly, referencedTypeDefs, spliceTypes, spliceExprs, bytes) : Expr =
let expr = unpickleExpr localAssembly referencedTypeDefs bytes (envClosed spliceTypes)
fillHolesInRawExpr spliceExprs expr
let cast (expr: Expr) : Expr<'T> =
checkTypesSR (typeof<'T>) (typeOf expr) "expr" (SR.GetString(SR.QtmmExprHasWrongType))
new Expr<'T>(expr.Tree,expr.CustomAttributes)
open Patterns
type Expr with
member x.Substitute substitution = substituteRaw substitution x
member x.GetFreeVars () = (freeInExpr x :> seq<_>)
member x.Type = typeOf x
static member AddressOf (target:Expr) =
mkAddressOf target
static member AddressSet (target:Expr, value:Expr) =
mkAddressSet (target,value)
static member Application (functionExpr:Expr, argument:Expr) =
mkApplication (functionExpr,argument)
static member Applications (functionExpr:Expr, arguments) =
mkApplications (functionExpr, arguments)
static member Call (methodInfo:MethodInfo, arguments) =
checkNonNull "methodInfo" methodInfo
mkStaticMethodCall (methodInfo, arguments)
static member Call (obj:Expr,methodInfo:MethodInfo, arguments) =
checkNonNull "methodInfo" methodInfo
mkInstanceMethodCall (obj,methodInfo,arguments)
static member Coerce (source:Expr, target:Type) =
checkNonNull "target" target
mkCoerce (target, source)
static member IfThenElse (guard:Expr, thenExpr:Expr, elseExpr:Expr) =
mkIfThenElse (guard, thenExpr, elseExpr)
static member ForIntegerRangeLoop (loopVariable, start:Expr, endExpr:Expr, body:Expr) =
mkForLoop(loopVariable, start, endExpr, body)
static member FieldGet (fieldInfo:FieldInfo) =
checkNonNull "fieldInfo" fieldInfo
mkStaticFieldGet fieldInfo
static member FieldGet (obj:Expr, fieldInfo:FieldInfo) =
checkNonNull "fieldInfo" fieldInfo
mkInstanceFieldGet (obj, fieldInfo)
static member FieldSet (fieldInfo:FieldInfo, value:Expr) =
checkNonNull "fieldInfo" fieldInfo
mkStaticFieldSet (fieldInfo, value)
static member FieldSet (obj:Expr, fieldInfo:FieldInfo, value:Expr) =
checkNonNull "fieldInfo" fieldInfo
mkInstanceFieldSet (obj, fieldInfo, value)
static member Lambda (parameter:Var, body:Expr) = mkLambda (parameter, body)
static member Let (letVariable:Var,letExpr:Expr,body:Expr) = mkLet (letVariable, letExpr, body)
static member LetRecursive (bindings, body:Expr) = mkLetRec (bindings, body)
static member NewObject (constructorInfo:ConstructorInfo, arguments) =
checkNonNull "constructorInfo" constructorInfo
mkCtorCall (constructorInfo, arguments)
static member DefaultValue (expressionType:Type) =
checkNonNull "expressionType" expressionType
mkDefaultValue expressionType
static member NewTuple elements =
mkNewTuple elements
static member NewRecord (recordType:Type, elements) =
checkNonNull "recordType" recordType
mkNewRecord (recordType, elements)
static member NewArray (elementType:Type, elements) =
checkNonNull "elementType" elementType
mkNewArray(elementType, elements)
static member NewDelegate (delegateType:Type, parameters: Var list, body: Expr) =
checkNonNull "delegateType" delegateType
mkNewDelegate(delegateType, mkIteratedLambdas (parameters, body))
static member NewUnionCase (unionCase, arguments) =
mkNewUnionCase (unionCase, arguments)
static member PropertyGet (obj:Expr, property: PropertyInfo, ?indexerArgs) =
checkNonNull "property" property
mkInstancePropGet (obj, property, defaultArg indexerArgs [])
static member PropertyGet (property: PropertyInfo, ?indexerArgs) =
checkNonNull "property" property
mkStaticPropGet (property, defaultArg indexerArgs [])
static member PropertySet (obj:Expr, property:PropertyInfo, value:Expr, ?indexerArgs) =
checkNonNull "property" property
mkInstancePropSet(obj, property, defaultArg indexerArgs [], value)
static member PropertySet (property:PropertyInfo, value:Expr, ?indexerArgs) =
mkStaticPropSet(property, defaultArg indexerArgs [], value)
static member Quote (inner:Expr) = mkQuote (inner, true)
static member QuoteRaw (inner:Expr) = mkQuote (inner, false)
static member QuoteTyped (inner:Expr) = mkQuote (inner, true)
static member Sequential (first:Expr, second:Expr) =
mkSequential (first, second)
static member TryWith (body:Expr, filterVar:Var, filterBody:Expr, catchVar:Var, catchBody:Expr) =
mkTryWith (body, filterVar, filterBody, catchVar, catchBody)
static member TryFinally (body:Expr, compensation:Expr) =
mkTryFinally (body, compensation)
static member TupleGet (tuple:Expr, index:int) =
mkTupleGet (typeOf tuple, index, tuple)
static member TypeTest (source: Expr, target: Type) =
checkNonNull "target" target
mkTypeTest (source, target)
static member UnionCaseTest (source:Expr, unionCase: UnionCaseInfo) =
mkUnionCaseTest (unionCase, source)
static member Value (value:'T) =
mkValue (box value, typeof<'T>)
static member Value(value: obj, expressionType: Type) =
checkNonNull "expressionType" expressionType
mkValue(value, expressionType)
static member ValueWithName (value:'T, name:string) =
checkNonNull "name" name
mkValueWithName (box value, typeof<'T>, name)
static member ValueWithName(value: obj, expressionType: Type, name:string) =
checkNonNull "expressionType" expressionType
checkNonNull "name" name
mkValueWithName(value, expressionType, name)
static member WithValue (value:'T, definition: Expr<'T>) =
let raw = mkValueWithDefn(box value, typeof<'T>, definition)
new Expr<'T>(raw.Tree,raw.CustomAttributes)
static member WithValue(value: obj, expressionType: Type, definition: Expr) =
checkNonNull "expressionType" expressionType
mkValueWithDefn (value, expressionType, definition)
static member Var(variable) =
mkVar(variable)
static member VarSet (variable, value:Expr) =
mkVarSet (variable, value)
static member WhileLoop (guard:Expr, body:Expr) =
mkWhileLoop (guard, body)
static member TryGetReflectedDefinition(methodBase:MethodBase) =
checkNonNull "methodBase" methodBase
tryGetReflectedDefinitionInstantiated(methodBase)
static member Cast(source:Expr) = cast source
static member Deserialize(qualifyingType:Type, spliceTypes, spliceExprs, bytes: byte[]) =
checkNonNull "qualifyingType" qualifyingType
checkNonNull "bytes" bytes
deserialize (qualifyingType, [| |], Array.ofList spliceTypes, Array.ofList spliceExprs, bytes)
static member Deserialize40(qualifyingType:Type, referencedTypes, spliceTypes, spliceExprs, bytes: byte[]) =
checkNonNull "spliceExprs" spliceExprs
checkNonNull "spliceTypes" spliceTypes
checkNonNull "referencedTypeDefs" referencedTypes
checkNonNull "qualifyingType" qualifyingType
checkNonNull "bytes" bytes
deserialize (qualifyingType, referencedTypes, spliceTypes, spliceExprs, bytes)
static member RegisterReflectedDefinitions(assembly, resource, serializedValue) =
Expr.RegisterReflectedDefinitions(assembly, resource, serializedValue, [| |])
static member RegisterReflectedDefinitions(assembly, resource, serializedValue, referencedTypes) =
checkNonNull "assembly" assembly
registerReflectedDefinitions(assembly, resource, serializedValue, referencedTypes)
static member GlobalVar<'T>(name) : Expr<'T> =
checkNonNull "name" name
Expr.Var(Var.Global(name, typeof<'T>)) |> Expr.Cast
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module DerivedPatterns =
open Patterns
[<CompiledName("BoolPattern")>]
let (|Bool|_|) input = match input with ValueObj(:? bool as v) -> Some(v) | _ -> None
[<CompiledName("StringPattern")>]
let (|String|_|) input = match input with ValueObj(:? string as v) -> Some(v) | _ -> None
[<CompiledName("SinglePattern")>]
let (|Single|_|) input = match input with ValueObj(:? single as v) -> Some(v) | _ -> None
[<CompiledName("DoublePattern")>]
let (|Double|_|) input = match input with ValueObj(:? double as v) -> Some(v) | _ -> None
[<CompiledName("CharPattern")>]
let (|Char|_|) input = match input with ValueObj(:? char as v) -> Some(v) | _ -> None
[<CompiledName("SBytePattern")>]
let (|SByte|_|) input = match input with ValueObj(:? sbyte as v) -> Some(v) | _ -> None
[<CompiledName("BytePattern")>]
let (|Byte|_|) input = match input with ValueObj(:? byte as v) -> Some(v) | _ -> None
[<CompiledName("Int16Pattern")>]
let (|Int16|_|) input = match input with ValueObj(:? int16 as v) -> Some(v) | _ -> None
[<CompiledName("UInt16Pattern")>]
let (|UInt16|_|) input = match input with ValueObj(:? uint16 as v) -> Some(v) | _ -> None
[<CompiledName("Int32Pattern")>]
let (|Int32|_|) input = match input with ValueObj(:? int32 as v) -> Some(v) | _ -> None
[<CompiledName("UInt32Pattern")>]
let (|UInt32|_|) input = match input with ValueObj(:? uint32 as v) -> Some(v) | _ -> None
[<CompiledName("Int64Pattern")>]
let (|Int64|_|) input = match input with ValueObj(:? int64 as v) -> Some(v) | _ -> None
[<CompiledName("UInt64Pattern")>]
let (|UInt64|_|) input = match input with ValueObj(:? uint64 as v) -> Some(v) | _ -> None
[<CompiledName("UnitPattern")>]
let (|Unit|_|) input = match input with Comb0(ValueOp(_,ty,None)) when ty = typeof<unit> -> Some() | _ -> None
/// (fun (x,y) -> z) is represented as 'fun p -> let x = p#0 let y = p#1' etc.
/// This reverses this encoding.
let (|TupledLambda|_|) (lam: Expr) =
/// Strip off the 'let' bindings for an TupledLambda
let rec stripSuccessiveProjLets (p:Var) n expr =
match expr with
| Let(v1,TupleGet(Var(pA),m),rest)
when p = pA && m = n->
let restvs,b = stripSuccessiveProjLets p (n+1) rest
v1::restvs, b
| _ -> ([],expr)
match lam.Tree with
| LambdaTerm(v,body) ->
match stripSuccessiveProjLets v 0 body with
| [],b -> Some([v], b)
| letvs,b -> Some(letvs,b)
| _ -> None
let (|TupledApplication|_|) e =
match e with
| Application(f,x) ->
match x with
| Unit -> Some(f,[])
| NewTuple(x) -> Some(f,x)
| x -> Some(f,[x])
| _ -> None
[<CompiledName("LambdasPattern")>]
let (|Lambdas|_|) (input: Expr) = qOneOrMoreRLinear (|TupledLambda|_|) input
[<CompiledName("ApplicationsPattern")>]
let (|Applications|_|) (input: Expr) = qOneOrMoreLLinear (|TupledApplication|_|) input
/// Reverse the compilation of And and Or
[<CompiledName("AndAlsoPattern")>]
let (|AndAlso|_|) input =
match input with
| IfThenElse(x,y,Bool(false)) -> Some(x,y)
| _ -> None
[<CompiledName("OrElsePattern")>]
let (|OrElse|_|) input =
match input with
| IfThenElse(x,Bool(true),y) -> Some(x,y)
| _ -> None
[<CompiledName("SpecificCallPattern")>]
let (|SpecificCall|_|) templateParameter =
// Note: precomputation
match templateParameter with
| (Lambdas(_,Call(_,minfo1,_)) | Call(_,minfo1,_)) ->
let isg1 = minfo1.IsGenericMethod
let gmd = if isg1 then minfo1.GetGenericMethodDefinition() else null
// end-of-precomputation
(fun tm ->
match tm with
| Call(obj,minfo2,args)
#if FX_NO_REFLECTION_METADATA_TOKENS
when ( // if metadata tokens are not available we'll rely only on equality of method references
#else
when (minfo1.MetadataToken = minfo2.MetadataToken &&
#endif
if isg1 then
minfo2.IsGenericMethod && gmd = minfo2.GetGenericMethodDefinition()
else
minfo1 = minfo2) ->
Some(obj,(minfo2.GetGenericArguments() |> Array.toList),args)
| _ -> None)
| _ ->
invalidArg "templateParameter" (SR.GetString(SR.QunrecognizedMethodCall))
let private new_decimal_info =
methodhandleof (fun (low, medium, high, isNegative, scale) -> LanguagePrimitives.IntrinsicFunctions.MakeDecimal low medium high isNegative scale)
|> System.Reflection.MethodInfo.GetMethodFromHandle
:?> MethodInfo
[<CompiledName("DecimalPattern")>]
let (|Decimal|_|) input =
match input with
| Call (None, mi, [Int32 low; Int32 medium; Int32 high; Bool isNegative; Byte scale])
when mi.Name = new_decimal_info.Name
&& mi.DeclaringType.FullName = new_decimal_info.DeclaringType.FullName ->
Some (LanguagePrimitives.IntrinsicFunctions.MakeDecimal low medium high isNegative scale)
| _ -> None
[<CompiledName("MethodWithReflectedDefinitionPattern")>]
let (|MethodWithReflectedDefinition|_|) (methodBase) =
Expr.TryGetReflectedDefinition(methodBase)
[<CompiledName("PropertyGetterWithReflectedDefinitionPattern")>]
let (|PropertyGetterWithReflectedDefinition|_|) (propertyInfo:System.Reflection.PropertyInfo) =
Expr.TryGetReflectedDefinition(propertyInfo.GetGetMethod(true))
[<CompiledName("PropertySetterWithReflectedDefinitionPattern")>]
let (|PropertySetterWithReflectedDefinition|_|) (propertyInfo:System.Reflection.PropertyInfo) =
Expr.TryGetReflectedDefinition(propertyInfo.GetSetMethod(true))
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module ExprShape =
open Patterns
let RebuildShapeCombination(shape:obj,arguments) =
// preserve the attributes
let op,attrs = unbox<ExprConstInfo * Expr list>(shape)
let e =
match op,arguments with
| AppOp,[f;x] -> mkApplication(f,x)
| IfThenElseOp,[g;t;e] -> mkIfThenElse(g,t,e)
| LetRecOp,[e1] -> mkLetRecRaw(e1)
| LetRecCombOp,_ -> mkLetRecCombRaw(arguments)
| LetOp,[e1;e2] -> mkLetRawWithCheck(e1,e2)
| NewRecordOp(ty),_ -> mkNewRecord(ty, arguments)
| NewUnionCaseOp(unionCase),_ -> mkNewUnionCase(unionCase, arguments)
| UnionCaseTestOp(unionCase),[arg] -> mkUnionCaseTest(unionCase,arg)
| NewTupleOp(ty),_ -> mkNewTupleWithType(ty, arguments)
| TupleGetOp(ty,i),[arg] -> mkTupleGet(ty,i,arg)
| InstancePropGetOp(pinfo),(obj::args) -> mkInstancePropGet(obj,pinfo,args)
| StaticPropGetOp(pinfo),_ -> mkStaticPropGet(pinfo,arguments)
| InstancePropSetOp(pinfo),obj::(FrontAndBack(args,v)) -> mkInstancePropSet(obj,pinfo,args,v)
| StaticPropSetOp(pinfo),(FrontAndBack(args,v)) -> mkStaticPropSet(pinfo,args,v)
| InstanceFieldGetOp(finfo),[obj] -> mkInstanceFieldGet(obj,finfo)
| StaticFieldGetOp(finfo),[] -> mkStaticFieldGet(finfo )
| InstanceFieldSetOp(finfo),[obj;v] -> mkInstanceFieldSet(obj,finfo,v)
| StaticFieldSetOp(finfo),[v] -> mkStaticFieldSet(finfo,v)
| NewObjectOp minfo,_ -> mkCtorCall(minfo,arguments)
| DefaultValueOp(ty),_ -> mkDefaultValue(ty)
| StaticMethodCallOp(minfo),_ -> mkStaticMethodCall(minfo,arguments)
| InstanceMethodCallOp(minfo),obj::args -> mkInstanceMethodCall(obj,minfo,args)
| CoerceOp(ty),[arg] -> mkCoerce(ty,arg)
| NewArrayOp(ty),_ -> mkNewArray(ty,arguments)
| NewDelegateOp(ty),[arg] -> mkNewDelegate(ty,arg)
| SequentialOp,[e1;e2] -> mkSequential(e1,e2)
| TypeTestOp(ty),[e1] -> mkTypeTest(e1,ty)
| AddressOfOp,[e1] -> mkAddressOf(e1)
| VarSetOp,[E(VarTerm(v)); e] -> mkVarSet(v,e)
| AddressSetOp,[e1;e2] -> mkAddressSet(e1,e2)
| ForIntegerRangeLoopOp,[e1;e2;E(LambdaTerm(v,e3))] -> mkForLoop(v,e1,e2,e3)
| WhileLoopOp,[e1;e2] -> mkWhileLoop(e1,e2)
| TryFinallyOp,[e1;e2] -> mkTryFinally(e1,e2)
| TryWithOp,[e1;Lambda(v1,e2);Lambda(v2,e3)] -> mkTryWith(e1,v1,e2,v2,e3)
| QuoteOp flg,[e1] -> mkQuote(e1,flg)
| ValueOp(v,ty,None),[] -> mkValue(v,ty)
| ValueOp(v,ty,Some nm),[] -> mkValueWithName(v,ty,nm)
| WithValueOp(v,ty),[e] -> mkValueWithDefn(v,ty,e)
| _ -> raise <| System.InvalidOperationException (SR.GetString(SR.QillFormedAppOrLet))
EA(e.Tree,attrs)
[<CompiledName("ShapePattern")>]
let rec (|ShapeVar|ShapeLambda|ShapeCombination|) input =
let rec loop expr =
let (E(t)) = expr
match t with
| VarTerm v -> ShapeVar(v)
| LambdaTerm(v,b) -> ShapeLambda(v,b)
| CombTerm(op,args) -> ShapeCombination(box<ExprConstInfo * Expr list> (op,expr.CustomAttributes),args)
| HoleTerm _ -> invalidArg "expr" (SR.GetString(SR.QunexpectedHole))
loop (input :> Expr)