From b362f7faf2b10fb19e063dad2f0e055f89f32ed3 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Fri, 5 Apr 2019 10:05:23 +0200 Subject: [PATCH 01/38] initial Quotations sketch --- src/Fable.Transforms/AST/AST.Fable.fs | 10 +- src/Fable.Transforms/FSharp2Fable.fs | 29 ++++- src/Fable.Transforms/Fable.Transforms.fsproj | 11 +- src/Fable.Transforms/Fable2Babel.fs | 56 ++++++++- src/Fable.Transforms/FableTransforms.fs | 2 + src/Fable.Transforms/Replacements.fs | 29 ++++- src/Fable.Transforms/Transforms.Util.fs | 1 + src/fable-library/ExprUtils.fs | 12 ++ src/fable-library/Fable.Library.fsproj | 15 +-- src/fable-library/Quotations.fs | 122 +++++++++++++++++++ tests/Main/ExprTests.fs | 92 ++++++++++++++ 11 files changed, 351 insertions(+), 28 deletions(-) create mode 100644 src/fable-library/ExprUtils.fs create mode 100644 src/fable-library/Quotations.fs create mode 100644 tests/Main/ExprTests.fs diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 8dbb408153..bcdc508e54 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -16,6 +16,7 @@ type Type = | Char | String | Regex + | Expr of gen : Option | Number of NumberKind | EnumType of kind: EnumTypeKind * fullName: string | Option of genericArg: Type @@ -29,7 +30,7 @@ type Type = member this.Generics = match this with - | Option gen | Array gen | List gen -> [gen] + | Expr (Some gen) | Option gen | Array gen | List gen -> [gen] | FunctionType(LambdaType argType, returnType) -> [argType; returnType] | FunctionType(DelegateType argTypes, returnType) -> argTypes @ [returnType] | Tuple gen -> gen @@ -38,6 +39,7 @@ type Type = | _ -> [] member this.ReplaceGenerics(newGen: Type list) = match this with + | Expr (Some _) -> Expr (Some newGen.Head) | Option _ -> Option newGen.Head | Array _ -> Array newGen.Head | List _ -> List newGen.Head @@ -302,8 +304,12 @@ type Expr = | TryCatch of body: Expr * catch: (Ident * Expr) option * finalizer: Expr option * range: SourceLocation option | IfThenElse of guardExpr: Expr * thenExpr: Expr * elseExpr: Expr * range: SourceLocation option + | Quote of typed : bool * value : Expr + member this.Type = match this with + | Quote(true, value) -> Expr(Some value.Type) + | Quote(false, _) -> Expr None | Test _ -> Boolean | Value(kind,_) -> kind.Type | IdentExpr id -> id.Type @@ -323,7 +329,7 @@ type Expr = | ObjectExpr _ | Sequential _ | Let _ | DecisionTree _ | DecisionTreeSuccess _ -> None - | Function(_,e,_) | TypeCast(e,_) -> e.Range + | Quote(_,e) | Function(_,e,_) | TypeCast(e,_) -> e.Range | IdentExpr id -> id.Range | Value(_,r) | IfThenElse(_,_,_,r) | TryCatch(_,_,_,r) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 7b2413bb99..3ccb6b65c0 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -734,9 +734,32 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = return sprintf "Cannot compile ILFieldGet(%A, %s)" ownerTyp fieldName |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) - | BasicPatterns.Quote _ -> - return "Quotes are not currently supported by Fable" - |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) + + | BasicPatterns.Quote expr -> + + + + // let rec test (e : FSharpExpr) = + // trampoline { + // match e with + // | BasicPatterns.Call(None, m, targs, margs, args) -> + // let! args = trampolineListMap test args + + + + // return failwith "" + // | _ -> + // return failwith "" + + // } + + + + + let! expr = transformExpr com ctx expr + return Fable.Quote(false, expr) + // return "Quotes are not currently supported by Fable" + // |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) // TODO: Ask. I see this when accessing Result types (all structs?) | BasicPatterns.AddressOf(expr) -> diff --git a/src/Fable.Transforms/Fable.Transforms.fsproj b/src/Fable.Transforms/Fable.Transforms.fsproj index a8fe7dd5a4..10ae22fd44 100644 --- a/src/Fable.Transforms/Fable.Transforms.fsproj +++ b/src/Fable.Transforms/Fable.Transforms.fsproj @@ -1,8 +1,6 @@ - netstandard2.0 - true @@ -23,13 +21,8 @@ - - - + - - + \ No newline at end of file diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 881ab021e7..bafb93b5aa 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -92,7 +92,7 @@ module Util = let rec isJsStatement ctx preferStatement (expr: Fable.Expr) = match expr with | Fable.Value _ | Fable.Import _ | Fable.DelayedResolution _ | Fable.Test _ | Fable.IdentExpr _ | Fable.Function _ - | Fable.ObjectExpr _ | Fable.Operation _ | Fable.Get _ | Fable.TypeCast _ -> false + | Fable.ObjectExpr _ | Fable.Operation _ | Fable.Get _ | Fable.TypeCast _ | Fable.Quote _ -> false | Fable.TryCatch _ | Fable.Debugger _ | Fable.Sequential _ | Fable.Let _ | Fable.Set _ @@ -463,6 +463,8 @@ module Util = | Fable.List gen -> genericTypeInfo "list" [|gen|] | Fable.Regex -> nonGenericTypeInfo Types.regex | Fable.MetaType -> nonGenericTypeInfo Types.type_ + | Fable.Expr None -> nonGenericTypeInfo "Expr" + | Fable.Expr (Some gen) -> genericTypeInfo "Expr" [|gen|] | Fable.DeclaredType(ent, generics) -> match ent, generics with | Replacements.BuiltinEntity kind -> @@ -856,6 +858,10 @@ module Util = coreLibCall com ctx None "Util" "isArray" [|com.TransformAsExpr(ctx, expr)|] | Fable.List _ -> jsInstanceof (coreValue com ctx "Types" "List") expr + + | Fable.Expr _ -> + coreLibCall com ctx None "ExprUtils" "isExpr" [| com.TransformAsExpr(ctx, expr) |] + | Replacements.Builtin kind -> match kind with | Replacements.BclGuid -> jsTypeof "string" expr @@ -1181,9 +1187,57 @@ module Util = | Fable.Debugger _ | Fable.Throw _ | Fable.Loop _ | Fable.TryCatch _ -> iife com ctx expr :> Expression + | Fable.Quote(_,expr) -> + let serializeExpr (expr : Fable.Expr) = + // TODO: proper serialization + string expr + + // let mk (name : string) (args : list) = + // coreLibCall com ctx None "ExprUtil" ("mk" + name) (List.toArray args) + + // let ident = + // { + // Fable.Ident.Name = "Expr" + // Fable.Ident.Type = Fable.Type.Expr None + // Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent + // Fable.Ident.IsMutable = false + // Fable.Ident.IsCompilerGenerated = false + // Fable.Ident.Range = None + // } + + + + + + + // let rec visit (variables : System.Collections.Generic.Dictionary) (e : Fable.Expr) = + // match e with + // | Fable.Function(Fable.Lambda a, b, name) -> + // variables.[a.Name] <- a + // coreLibCall com ctx None "ExprUtil" "mkLambda" [| Identifier(a.Name, ?loc = a.Range); visit variables b |] + + // | Fable.Operation(Fable.OperationKind.Call(call, info), t, r) -> + // failwith "" + + // // //| Operation of OperationKind * typ: Type * range: SourceLocation option + // // let a = Fable.Get(Fable.IdentExpr ident, Fable.GetKind.FieldGet("Function", false, Fable.FunctionType(Fable.Function)), Fable.Function, None) + // // Fable.Operation(Fable.StaticCall (Fable.Get(Fable.IdentExpr ident, Fable.GetKind.FieldGet "Function")) , Fable.Type.Expr None, None) + // // failwith "" + + // | _ -> + // failwith "" + + + + let literal = StringLiteral(serializeExpr expr) + coreLibCall com ctx None "ExprUtils" "deserialize" [| literal |] + let rec transformAsStatements (com: IBabelCompiler) ctx returnStrategy (expr: Fable.Expr): Statement array = match expr with + | Fable.Quote _ -> + [| transformAsExpr com ctx expr |> resolveExpr expr.Type returnStrategy |] + | Fable.TypeCast(e, t) -> [|transformCast com ctx t e |> resolveExpr t returnStrategy|] diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 6200c0e96c..286a7c7cd7 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -7,6 +7,7 @@ open FSharp.Compiler.SourceCodeServices // TODO: Use trampoline here? let visit f e = match e with + | Quote(b, e) -> Quote(b, f e) | IdentExpr _ | Debugger _ -> e | TypeCast(e, t) -> TypeCast(f e, t) | Import(e1, e2, kind, t, r) -> Import(f e1, f e2, kind, t, r) @@ -109,6 +110,7 @@ let rec visitFromOutsideIn (f: Expr->Expr option) e = visit (visitFromOutsideIn f) e let getSubExpressions = function + | Quote(_,e) -> [e] | IdentExpr _ | Debugger _ -> [] | TypeCast(e,_) -> [e] | Import(e1,e2,_,_,_) -> [e1;e2] diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 8495d8e324..5e2959c484 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -665,7 +665,7 @@ let applyOp (com: ICompiler) (ctx: Context) r t opName (args: Expr list) argType let isCompatibleWithJsComparison = function | Builtin(BclInt64|BclUInt64|BclBigInt) - | Array _ | List _ | Tuple _ | Option _ | MetaType -> false + | Array _ | List _ | Tuple _ | Option _ | MetaType | Expr _ -> false | Builtin(BclGuid|BclTimeSpan) -> true // TODO: Non-record/union declared types without custom equality // should be compatible with JS comparison @@ -1721,7 +1721,6 @@ let maps (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Exp let mangledName = Naming.buildNameWithoutSanitationFrom "FSharpMap" isStatic i.CompiledName i.OverloadSuffix.Value let args = injectArg com ctx r "Map" mangledName i.GenericArgs args Helper.CoreCall("Map", mangledName, t, args, i.SignatureArgTypes, ?thisArg=thisArg, ?loc=r) |> Some - let mapModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Expr option) (args: Expr list) = let meth = Naming.lowerFirst i.CompiledName let args = injectArg com ctx r "Map" meth i.GenericArgs args @@ -2461,6 +2460,29 @@ let events (_: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Exp | meth, Some x -> Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some | meth, None -> Helper.CoreCall("Event", Naming.lowerFirst meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some +let exprs (name : string) (_: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr list) = + match i.CompiledName, thisArg, args with + //| meth, Some x,_ -> Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some + | "Value", None, _ -> + match args with + | [a] -> + let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None + Helper.CoreCall("Quotations", name, t, [a; makeTypeInfo None a.Type], i.SignatureArgTypes, ?loc=r) |> Some + | _ -> + let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None + Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + + + | meth, Some x,_ -> + let _,name = getMangledNames i (Some x) + Helper.CoreCall("Quotations", name, t, x :: args, i.SignatureArgTypes, ?loc=r) |> Some + + + | meth, None,_ -> + let _,name = getMangledNames i None + Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + + let observable (_: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Expr option) (args: Expr list) = Helper.CoreCall("Observable", Naming.lowerFirst i.CompiledName, t, args, i.SignatureArgTypes, ?loc=r) |> Some @@ -2657,6 +2679,9 @@ let tryField returnTyp ownerTyp fieldName = let private replacedModules = dict [ + "Microsoft.FSharp.Quotations.FSharpExpr", exprs "Expr" + "Microsoft.FSharp.Quotations.FSharpVar", exprs "Var" + "Microsoft.FSharp.Quotations.PatternsModule", exprs "Patterns" "System.Math", operators "Microsoft.FSharp.Core.Operators", operators "Microsoft.FSharp.Core.Operators.Checked", operators diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs index 6ddc7be18d..01555804ea 100644 --- a/src/Fable.Transforms/Transforms.Util.fs +++ b/src/Fable.Transforms/Transforms.Util.fs @@ -442,6 +442,7 @@ module AST = | Boolean -> Types.bool | Char -> Types.char | String -> Types.string + | Expr _ -> "Expr" // TODO: Type info forErasedUnion? | ErasedUnion _ | Any -> Types.object | Number kind -> diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs new file mode 100644 index 0000000000..12f6354744 --- /dev/null +++ b/src/fable-library/ExprUtils.fs @@ -0,0 +1,12 @@ +module ExprUtils + +open Quotations + +let deserialize (str : string) = + let v = Var("a", typeof) + Expr.Let(v, Expr.Value(1, typeof), Expr.Var v) + +let isExpr (o : obj) = + match o with + | :? Expr -> true + | _ -> false \ No newline at end of file diff --git a/src/fable-library/Fable.Library.fsproj b/src/fable-library/Fable.Library.fsproj index ce01c1afdc..bd099f485d 100644 --- a/src/fable-library/Fable.Library.fsproj +++ b/src/fable-library/Fable.Library.fsproj @@ -1,20 +1,12 @@ - - + netstandard2.0 - $(DefineConstants);FABLE_COMPILER - $(DefineConstants);FX_NO_BIGINT - - - - @@ -22,9 +14,10 @@ + + - - + \ No newline at end of file diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs new file mode 100644 index 0000000000..1a80a0ed6e --- /dev/null +++ b/src/fable-library/Quotations.fs @@ -0,0 +1,122 @@ +module Quotations + +open Fable.Core + +[] +type Var(name : string, typ : System.Type, ?isMutable : bool) = + let isMutable = match isMutable with | None -> false | Some m -> m + static let mutable currentStamp = 0 + let stamp = + let v = currentStamp + currentStamp <- currentStamp + 1 + v + + member internal x.Stamp = stamp + member x.Name = name + member x.Type = typ + member x.IsMutable = isMutable + override x.GetHashCode() = stamp + override x.Equals o = + match o with + | :? Var as o -> o.Stamp = stamp + | _ -> false + + override x.ToString() = name + + interface System.IComparable with + member x.CompareTo(o : obj) = + match o with + | :? Var as o -> + let c = compare name o.Name + if c <> 0 then c + else + let c = compare typ.Name o.Type.Name + if c <> 0 then c + else compare stamp o.Stamp + | _ -> failwith "Var can only be compared to itself" + +type ExprConstInfo = + | AppOp + | LetOp + | ValueOp of obj * System.Type * Option + | IfThenElseOp + +and Tree = + | CombTerm of ExprConstInfo * Expr list + | VarTerm of Var + | LambdaTerm of Var * Expr + + +and [] Expr(tree : Tree) = + static let (|E|) (e : Expr) = E (e.Tree) + member internal x.Tree = tree + + + override x.ToString() = + match tree with + | VarTerm v -> sprintf "Var %s" v.Name + | CombTerm(ValueOp(_,v,None),[]) -> sprintf "Value %A" v + | CombTerm(ValueOp(_,v,Some n),[])-> sprintf "ValueWithName(%A, %s)" v n + | CombTerm(LetOp, [e; E (LambdaTerm(v, b))]) -> sprintf "Let(%A, %s, %s)" v (string e) (string b) + | _ -> "bad expression" + + [] + static member Value(o : obj, t : System.Type) = + Expr(CombTerm(ValueOp(o, t, None), [])) + + static member Var(v : Var) = + Expr(VarTerm v) + static member Lambda(v : Var, b : Expr) = + Expr(LambdaTerm(v, b)) + + static member Application(f : Expr, e : Expr) = + Expr(CombTerm(AppOp, [f; e])) + + static member Let(v : Var, e : Expr, b : Expr) = + Expr(CombTerm(LetOp, [e; Expr(LambdaTerm(v, b))])) + static member IfThenElse(c : Expr, i : Expr, e : Expr) = + Expr(CombTerm(IfThenElseOp, [c; i; e])) + + +[] +module Patterns = + + let inline (|E|) (e : Expr) = E (e.Tree) + + [] + let (|Value|_|) (e : Expr) = + match e with + | E (CombTerm(ValueOp(v, t, _), [])) -> Some (v,t) + | _ -> None + + + [] + let (|Var|_|) (e : Expr) = + match e with + | E (VarTerm(v)) -> Some (v) + | _ -> None + + + [] + let (|Lambda|_|) (e : Expr) = + match e with + | E(LambdaTerm(v, b)) -> Some (v,b) + | _ -> None + + [] + let (|Application|_|) (e : Expr) = + match e with + | E(CombTerm(AppOp, [f;e])) -> Some (f, e) + | _ -> None + + [] + let (|Let|_|) (e : Expr) = + match e with + | E(CombTerm(LetOp, [e; E(LambdaTerm(v, b))])) -> Some (v,e,b) + | _ -> None + + [] + let (|IfThenElse|_|) (e : Expr) = + match e with + | E(CombTerm(IfThenElseOp, [c; i; e])) -> Some (c,i,e) + | _ -> None \ No newline at end of file diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs new file mode 100644 index 0000000000..3a58bbf755 --- /dev/null +++ b/tests/Main/ExprTests.fs @@ -0,0 +1,92 @@ +module Fable.Tests.Expr + +open System +open Util.Testing +open Fable.Tests +open System.Globalization +open FSharp.Quotations.Patterns + +type Bla() = + member x.Delay (f : unit -> 'a) = f() + +type SeppBuilder() = + inherit Bla() + member x.Quote() = () + + member x.Return v = v + + +let sepp = SeppBuilder() + +type V2 = { x : int; y : int } with + static member (+) (l : V2, r : V2) = { x = l.x + r.x; y = l.y + r.y } + +open FSharp.Quotations +open FSharp.Quotations.Patterns + +let tests = + testList "Expr" [ + + + testCase "Var constructions" <| fun () -> + let a = Var("a", typeof, true) + let b = Var("b", typeof) + let a2 = Var("a", typeof, true) + + equal a.Name "a" + equal b.Name "b" + equal a.IsMutable true + equal b.IsMutable false + equal a.Type typeof + equal b.Type typeof + + equal a a + equal b b + equal a2 a2 + notEqual a a2 + + testCase "Expr.Value" <| fun () -> + let e = Expr.Value(10, typeof) + match e with + | Value((:? int as o),t) -> + equal o 10 + equal typeof t + | _ -> failwith "not a value" + + testCase "Expr.Value<'a>" <| fun () -> + let e = Expr.Value(10) + match e with + | Value((:? int as o),t) -> + equal o 10 + equal typeof t + | _ -> failwith "not a value" + + testCase "Expr.Var" <| fun () -> + let v = Var("a", typeof) + match Expr.Var v with + | Var v1 -> equal v v1 + | _ -> failwith "not a var" + + testCase "Expr.Lambda" <| fun () -> + let v = Var("a", typeof) + match Expr.Lambda(v, Expr.Var v) with + | Lambda(v1, _) -> equal v v1 + | _ -> failwith "not a lambda" + + testCase "Expr.Application" <| fun () -> + let v = Var("a", typeof int>) + match Expr.Application(Expr.Var v, Expr.Value 10) with + | Application(v1, _) -> () + | _ -> failwith "not a lambda" + + testCase "Expr.IfThenElse" <| fun () -> + let v = Var("a", typeof) + match Expr.IfThenElse(Expr.Var v, Expr.Value 10, Expr.Value 3) with + | IfThenElse _ -> () + | _ -> failwith "not an ifthenelse" + + + + + + ] \ No newline at end of file From 2924407d31c5cde3536be998ac07ab0eafe63d26 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Fri, 5 Apr 2019 10:47:55 +0200 Subject: [PATCH 02/38] added some Expr constructors --- src/Fable.Transforms/Replacements.fs | 2 + src/fable-library/Quotations.fs | 86 +++++++++++++++++++++++++++- tests/Main/ExprTests.fs | 43 ++++++++++++-- 3 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 5e2959c484..807a345c3f 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2630,6 +2630,8 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio let fsharpType methName (r: SourceLocation option) t (i: CallInfo) (args: Expr list) = match methName with + | "MakeFunctionType" -> + Helper.CoreCall("Reflection", "lambda", t, args, i.SignatureArgTypes, ?loc=r) |> Some | "MakeTupleType" -> Helper.CoreCall("Reflection", "tuple", t, args, i.SignatureArgTypes, ?loc=r) |> Some // Prevent name clash with FSharpValue.GetRecordFields diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 1a80a0ed6e..5349673013 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -1,6 +1,7 @@ module Quotations open Fable.Core +open FSharp.Reflection [] type Var(name : string, typ : System.Type, ?isMutable : bool) = @@ -33,11 +34,39 @@ type Var(name : string, typ : System.Type, ?isMutable : bool) = let c = compare typ.Name o.Type.Name if c <> 0 then c else compare stamp o.Stamp - | _ -> failwith "Var can only be compared to itself" + | _ -> + failwith "Var can only be compared to Var" + + +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 type ExprConstInfo = | AppOp | LetOp + | LetRecOp + | LetRecCombOp | ValueOp of obj * System.Type * Option | IfThenElseOp @@ -49,8 +78,33 @@ and Tree = and [] Expr(tree : Tree) = static let (|E|) (e : Expr) = E (e.Tree) + static let (|L0|) (l : list<_>) = match l with [] -> L0 | _ -> failwith "bad list" + static let (|L1|) (l : list<_>) = match l with [a] -> L1(a) | _ -> failwith "bad list" + static let (|L2|) (l : list<_>) = match l with [a;b] -> L2(a,b) | _ -> failwith "bad list" + static let (|L3|) (l : list<_>) = match l with [a;b;c] -> L3(a,b,c) | _ -> failwith "bad list" + + static let (|Lambda|_|) (e : Expr) = match e.Tree with | LambdaTerm(v,b) -> Some (v,b) | _ -> None + static let (|IteratedLambda|_|) (e: Expr) = Helpers.qOneOrMoreRLinear (|Lambda|_|) e + static let rec typeOf (e : Expr) = + match e.Tree with + | VarTerm v -> v.Type + | LambdaTerm(v, b) -> FSharpType.MakeFunctionType(v.Type, typeOf b) + | CombTerm(t, c) -> + match t, c with + | AppOp, L2(f,_e) -> FSharpType.GetFunctionElements (typeOf f) |> snd + | LetOp, L3(_,_,b) -> typeOf b + | ValueOp(_,t,_), _ -> t + | IfThenElseOp, L3(_,i,_) -> typeOf i + + | LetRecOp, [IteratedLambda(_, E(CombTerm(LetRecCombOp, b2::_)))] -> typeOf b2 + | LetRecOp, _ -> failwith "bad" + | LetRecCombOp, _ -> failwith "bad" + + + member internal x.Tree = tree + member x.Type = typeOf x override x.ToString() = match tree with @@ -74,6 +128,23 @@ and [, body : Expr) = + match bindings with + | [] -> body + | [(v,e)] -> Expr.Let(v, e, body) + | many -> + let rec acc (all : list) (l : list) = + match l with + | (v,_e) :: rest -> + let rest = acc all rest + Expr.Lambda(v, rest) + | [] -> + Expr(CombTerm(LetRecCombOp, body :: (List.map snd all))) + Expr(CombTerm(LetRecOp, [acc many many])) + //| CombTerm(LetRecOp, [IteratedLambda(vs, E(CombTerm(LetRecCombOp, b2::bs)))]) -> Some(List.zip vs bs, b2) + static member IfThenElse(c : Expr, i : Expr, e : Expr) = Expr(CombTerm(IfThenElseOp, [c; i; e])) @@ -115,8 +186,19 @@ module Patterns = | E(CombTerm(LetOp, [e; E(LambdaTerm(v, b))])) -> Some (v,e,b) | _ -> None + + let private (|IteratedLambda|_|) (e: Expr) = Helpers.qOneOrMoreRLinear (|Lambda|_|) e + + [] + let (|LetRecursive|_|) (e : Expr) = + match e.Tree with + | CombTerm(LetRecOp, [IteratedLambda(vs, E(CombTerm(LetRecCombOp, b2::bs)))]) -> Some(List.zip vs bs, b2) + | _ -> None + [] let (|IfThenElse|_|) (e : Expr) = match e with | E(CombTerm(IfThenElseOp, [c; i; e])) -> Some (c,i,e) - | _ -> None \ No newline at end of file + | _ -> None + + \ No newline at end of file diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 3a58bbf755..c3e40ae357 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -47,6 +47,7 @@ let tests = testCase "Expr.Value" <| fun () -> let e = Expr.Value(10, typeof) + equal e.Type typeof match e with | Value((:? int as o),t) -> equal o 10 @@ -55,6 +56,7 @@ let tests = testCase "Expr.Value<'a>" <| fun () -> let e = Expr.Value(10) + equal e.Type typeof match e with | Value((:? int as o),t) -> equal o 10 @@ -63,19 +65,25 @@ let tests = testCase "Expr.Var" <| fun () -> let v = Var("a", typeof) - match Expr.Var v with + let e = Expr.Var v + equal e.Type typeof + match e with | Var v1 -> equal v v1 | _ -> failwith "not a var" testCase "Expr.Lambda" <| fun () -> let v = Var("a", typeof) - match Expr.Lambda(v, Expr.Var v) with + let e = Expr.Lambda(v, Expr.Var v) + equal e.Type typeof int> + match e with | Lambda(v1, _) -> equal v v1 | _ -> failwith "not a lambda" testCase "Expr.Application" <| fun () -> - let v = Var("a", typeof int>) - match Expr.Application(Expr.Var v, Expr.Value 10) with + let v = Var("a", typeof float>) + let e = Expr.Application(Expr.Var v, Expr.Value 10) + equal e.Type typeof + match e with | Application(v1, _) -> () | _ -> failwith "not a lambda" @@ -86,6 +94,33 @@ let tests = | _ -> failwith "not an ifthenelse" + testCase "Expr.Let" <| fun () -> + let v = Var("a", typeof) + let e = Expr.Let(v, Expr.Value 100, Expr.Var v) + match e with + | Let(v1, Value _, Var _) -> equal v v1 + | _ -> failwith "bad let binding" + + + testCase "Expr.LetRecursive" <| fun () -> + let bindings = + [ + Var("a", typeof), Expr.Value 10.0 + Var("b", typeof), Expr.Value true + ] + let e = Expr.LetRecursive(bindings, Expr.Value 100) + equal e.Type typeof + match e with + | LetRecursive([a, va; b, vb], _) -> + equal a.Name "a" + equal a.Type va.Type + equal a.Type typeof + + equal b.Name "b" + equal b.Type vb.Type + equal b.Type typeof + | _ -> failwith "bad recursive binding" + From 0bb535bd9963463ab4c8670c4888fc2b11230b09 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sat, 6 Apr 2019 17:27:14 +0200 Subject: [PATCH 03/38] Reflection sync --- src/Fable.Transforms/AST/AST.Fable.fs | 30 ++ src/Fable.Transforms/FSharp2Fable.fs | 135 ++++++- src/Fable.Transforms/Fable2Babel.fs | 146 ++++++-- src/Fable.Transforms/Replacements.fs | 146 +++++--- src/fable-library/Quotations.fs | 14 +- src/fable-library/Reflection.ts | 488 +++++++++++++++++++++++++- tests/Main/ExprTests.fs | 56 ++- 7 files changed, 920 insertions(+), 95 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index bcdc508e54..729114fc36 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -4,6 +4,7 @@ open Fable open Fable.AST open FSharp.Compiler.SourceCodeServices open System +open System.Reflection type EnumTypeKind = NumberEnumType | StringEnumType type FunctionTypeKind = LambdaType of Type | DelegateType of Type list @@ -54,6 +55,31 @@ type Type = | DeclaredType(ent,_) -> DeclaredType(ent,newGen) | t -> t + +type ParameterInfo = + { + Name : string + Type : Type + } + +type MemberInfoKind = + | Property of name : string * typ : Type * fsharp : bool * isStatic : bool + | Field of name : string * typ : Type * isStatic : bool + | Method of name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string + | Constructor of parameters : ParameterInfo[] * mangledName : string + +type MemberInfo = + { + Kind : MemberInfoKind + Attributes : array + } + +type UnionCaseInfo = + { + Name : string + Fields : MemberInfo[] + } + type ValueDeclarationInfo = { Name: string IsPublic: bool @@ -64,6 +90,7 @@ type ValueDeclarationInfo = type ClassImplicitConstructorInfo = { Name: string Entity: FSharpEntity + Members : MemberInfo[] EntityName: string IsEntityPublic: bool IsConstructorPublic: bool @@ -75,11 +102,14 @@ type ClassImplicitConstructorInfo = type UnionConstructorInfo = { Entity: FSharpEntity + Cases : UnionCaseInfo[] + Members : MemberInfo[] EntityName: string IsPublic: bool } type CompilerGeneratedConstructorInfo = { Entity: FSharpEntity + Members : MemberInfo[] EntityName: string IsPublic: bool } diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 3ccb6b65c0..f8def57d34 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -27,7 +27,7 @@ let private checkArgumentsPassedByRef com ctx (args: FSharpExpr list) = |> addWarning com ctx.InlinePath (makeRangeFrom arg) | _ -> () -let private transformBaseConsCall com ctx r baseEnt (baseCons: FSharpMemberOrFunctionOrValue) genArgs baseArgs = +let private transformBaseConsCall com (ctx : Context) r baseEnt (baseCons: FSharpMemberOrFunctionOrValue) genArgs baseArgs = let thisArg = ctx.BoundConstructorThis |> Option.map Fable.IdentExpr let baseArgs = transformExprList com ctx baseArgs |> run let genArgs = genArgs |> Seq.map (makeType com ctx.GenericArgs) @@ -48,7 +48,7 @@ let private transformBaseConsCall com ctx r baseEnt (baseCons: FSharpMemberOrFun let baseCons = makeCallFrom com ctx r Fable.Unit true genArgs thisArg baseArgs baseCons entityRefMaybeGlobalOrImported com baseEnt, baseCons -let private transformNewUnion com ctx r fsType +let private transformNewUnion com ctx r (fsType : FSharpType) (unionCase: FSharpUnionCase) (argExprs: Fable.Expr list) = match fsType with | ErasedUnion(_, genArgs) -> @@ -788,6 +788,126 @@ let private isIgnoredMember (meth: FSharpMemberOrFunctionOrValue) = | Some ent -> isErasedEntity ent | None -> false) +let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = + let types, args = a.ConstructorArguments |> Seq.toArray |> Array.unzip + let ctor = + a.AttributeType.MembersFunctionsAndValues |> Seq.tryPick (fun m -> + if m.IsConstructor then + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.map (fun p -> p.Type)|> Seq.toArray + if args.Length = pars.Length then + if FSharp.Collections.Array.forall2 (fun ta tp -> tp = ta) types pars then Some m + else None + else + None + else + None + ) + match ctor with + | Some ctor -> + let args = + a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> + match v with + | :? string as str -> Fable.Value(Fable.StringConstant str, None) + | _ -> Fable.Value(Fable.StringConstant (string v), None) + ) + let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType + let x = makeCallFrom com ctx None typ false [] None args ctor + match a.AttributeType.TryFullName with + | Some n -> Some (n,x) + | _ -> None + | _ -> + None + +let private transformUnionCases (com:IFableCompiler) ctx (cases : seq) = + cases |> Seq.toArray |> Array.map (fun c -> + let fields = + c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> + { + Fable.MemberInfo.Kind = Fable.Property(f.Name, makeType com ctx.GenericArgs f.FieldType, true, false) + Fable.Attributes = f.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + ) + + { Fable.UnionCaseInfo.Name = c.Name; Fable.UnionCaseInfo.Fields = fields } + ) + +let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FSharpEntity) = + let realMembers = + ent.TryGetMembersFunctionsAndValues |> Seq.choose (fun m -> + + if m.IsEvent || m.IsEventAddMethod || m.IsEventRemoveMethod then + // TODO: support these? + None + elif m.IsValue then + Some { + Fable.MemberInfo.Kind = Fable.Field(m.CompiledName, makeType com ctx.GenericArgs m.FullType, not m.IsInstanceMember) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsProperty then + Some { + Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, makeType com ctx.GenericArgs m.ReturnParameter.Type, false, not m.IsInstanceMember) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsConstructor then + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> + { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + ) + + let mangledName = Helpers.getMemberDeclarationName com m + + Some { + Fable.MemberInfo.Kind = Fable.Constructor(pars, mangledName) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + + else // m.IsPropertyGetterMethod || m.IsPropertySetterMethod || m.IsValCompiledAsMethod || m.IsInstanceMember then + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> + { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + ) + let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type + + let mangledName = Helpers.getMemberDeclarationName com m + + Some { + Fable.MemberInfo.Kind = Fable.Method(m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + // else + // None + ) + + let special = + if ent.IsFSharpRecord then + let fields = ent.FSharpFields |> Seq.toArray + let parameters = fields |> Array.map (fun f -> { Fable.ParameterInfo.Name = f.Name; Fable.Type = makeType com ctx.GenericArgs f.FieldType}) + let ctor = "new " + getEntityDeclarationName com ent + Seq.concat [ + ent.FSharpFields |> Seq.map (fun m -> + { + Fable.MemberInfo.Kind = Fable.Property(m.Name, makeType com ctx.GenericArgs m.FieldType, true, false) + Fable.Attributes = m.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + ) + + Seq.singleton { + Fable.MemberInfo.Kind = Fable.Constructor(parameters, ctor) + Fable.Attributes = [||] + } + + ] + elif ent.IsFSharpUnion then + // let caseInfos = + // ent.UnionCases |> Seq.collect (fun c -> + // Seq.empty + // ) + Seq.empty + else + Seq.empty + + Seq.append realMembers special |> Seq.toArray + let private transformImplicitConstructor com (ctx: Context) (memb: FSharpMemberOrFunctionOrValue) args (body: FSharpExpr) = match memb.DeclaringEntity with @@ -821,6 +941,7 @@ let private transformImplicitConstructor com (ctx: Context) let info: Fable.ClassImplicitConstructorInfo = { Name = name Entity = ent + Members = transformMemberReflectionInfos com ctx ent EntityName = entityName IsEntityPublic = isPublicEntity ent IsConstructorPublic = isPublicMember memb @@ -1015,8 +1136,12 @@ let private transformDeclarations (com: FableCompiler) ctx rootEnt rootDecls = let entityName = getEntityDeclarationName com ent com.AddUsedVarName(entityName) // TODO: Check Equality/Comparison attributes + let props = transformMemberReflectionInfos com ctx ent + let info: Fable.UnionConstructorInfo = { Entity = ent + Members = props + Cases = transformUnionCases com ctx ent.UnionCases EntityName = entityName IsPublic = isPublicEntity ent } let r = getEntityLocation ent |> makeRange @@ -1027,11 +1152,15 @@ let private transformDeclarations (com: FableCompiler) ctx rootEnt rootDecls = let entityName = getEntityDeclarationName com ent com.AddUsedVarName(entityName) // TODO: Check Equality/Comparison attributes + let props = transformMemberReflectionInfos com ctx ent let info: Fable.CompilerGeneratedConstructorInfo = { Entity = ent EntityName = entityName - IsPublic = isPublicEntity ent } + IsPublic = isPublicEntity ent + Members = props } let r = getEntityLocation ent |> makeRange + + [Fable.ConstructorDeclaration(Fable.CompilerGeneratedConstructor info, Some r)] | _ -> transformDeclarationsInner com { ctx with EnclosingEntity = Some ent } sub diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index bafb93b5aa..adbcf04da2 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -364,25 +364,97 @@ module Util = | Fable.AsPojo(expr, caseRule) -> com.TransformAsExpr(ctx, Replacements.makePojo com r caseRule expr) | Fable.Curry(expr, arity) -> com.TransformAsExpr(ctx, Replacements.curryExprAtRuntime arity expr) - let rec transformRecordReflectionInfo com ctx r (ent: FSharpEntity) generics = + let rec transformMemberReflectionInfos (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = + let genMap = + let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray + Array.zip genParamNames generics |> Map + mems |> Array.choose (fun x -> + let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> + let value = com.TransformAsExpr(ctx, e) + let typ = StringLiteral(fullname) //com.TransformAsExpr(ctx, Fable.Value(Fable.TypeInfo e.Type, None)) + + ObjectExpression [| + U3.Case1 (ObjectProperty(StringLiteral "AttributeType", typ)) + U3.Case1 (ObjectProperty(StringLiteral "AttributeValue", value)) + |] :> Expression + )) + + match x.Kind with + | Fable.MemberInfoKind.Constructor(pars, mangledName) -> + let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) + let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit + + let invoke = + let args = pars |> Array.map (fun p -> Identifier(p.Name)) + let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) + + let res = + ArrayExpression [| + StringLiteral ".ctor"; ret; BooleanLiteral true; NumericLiteral(3.0); + ArrayExpression parInfos; + attributes + invoke + |] :> Expression + Some res + + | Fable.MemberInfoKind.Method(name, pars, ret, isStatic, mangledName) -> + let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) + let ret = transformTypeInfo com ctx r [||] genMap ret + + + let invoke = + let args = pars |> Array.map (fun p -> Identifier(p.Name)) + let args = + if isStatic then args + else Array.append [| Identifier "__self" |] args + + let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) + + let res = + ArrayExpression [| + StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(4.0); + ArrayExpression parInfos; + attributes + invoke + |] :> Expression + Some res + | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> + let kind = if fsharp then 2.0 else 0.0 + let ret = transformTypeInfo com ctx r [||] genMap typ + let res = + ArrayExpression [| + StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(kind); + ArrayExpression [||]; + attributes + |] :> Expression + Some res + | Fable.MemberInfoKind.Field(name, typ, isStatic) -> + let ret = transformTypeInfo com ctx r [||] genMap typ + let res = + ArrayExpression [| + StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(1.0); + ArrayExpression [||]; + attributes + |] :> Expression + Some res + ) + and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression let genMap = let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray Array.zip genParamNames generics |> Map - let fields = - ent.FSharpFields |> Seq.map (fun x -> - let typeInfo = - FSharp2Fable.TypeHelpers.makeType com Map.empty x.FieldType - |> transformTypeInfo com ctx r genMap - (ArrayExpression [|StringLiteral x.Name; typeInfo|] :> Expression)) - |> Seq.toArray - let fields = ArrowFunctionExpression([||], ArrayExpression fields :> Expression |> U2.Case2) :> Expression + let members = transformMemberReflectionInfos com ctx r ent mems generics + let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; fields|] |> coreLibCall com ctx None "Reflection" "record" - and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) generics = + and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression let genMap = @@ -393,7 +465,7 @@ module Util = let fieldTypes = uci.UnionCaseFields |> Seq.map (fun fi -> FSharp2Fable.TypeHelpers.makeType com Map.empty fi.FieldType - |> transformTypeInfo com ctx r genMap) |> Seq.toArray + |> transformTypeInfo com ctx r mems genMap) |> Seq.toArray let caseInfo = if fieldTypes.Length = 0 then getUnionCaseName uci |> StringLiteral :> Expression @@ -407,7 +479,7 @@ module Util = [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; cases|] |> coreLibCall com ctx None "Reflection" "union" - and transformTypeInfo (com: IBabelCompiler) ctx r (genMap: Map) t: Expression = + and transformTypeInfo (com: IBabelCompiler) ctx r (mems : Fable.MemberInfo[]) (genMap: Map) t: Expression = let error msg = addErrorAndReturnNull com r msg let primitiveTypeInfo name = @@ -416,7 +488,7 @@ module Util = [| StringLiteral fullname :> Expression |] |> coreLibCall com ctx None "Reflection" "type" let resolveGenerics generics: Expression[] = - generics |> Array.map (transformTypeInfo com ctx r genMap) + generics |> Array.map (transformTypeInfo com ctx r [||] genMap) let genericTypeInfo name genArgs = let resolved = resolveGenerics genArgs coreLibCall com ctx None "Reflection" name resolved @@ -431,9 +503,7 @@ module Util = | Fable.GenericParam name -> match Map.tryFind name genMap with | Some t -> t - | None -> - Replacements.genericTypeInfoError name |> addError com [] r - NullLiteral () :> Expression + | None -> coreLibCall com ctx None "Reflection" "getGenericParamter" [|StringLiteral name|] | Fable.Unit -> primitiveTypeInfo "unit" | Fable.Boolean -> primitiveTypeInfo "bool" | Fable.Char -> primitiveTypeInfo "char" @@ -480,25 +550,25 @@ module Util = | Replacements.BclBigInt -> genericEntity ent [||] | Replacements.BclHashSet gen | Replacements.FSharpSet gen -> - genericEntity ent [|transformTypeInfo com ctx r genMap gen|] + genericEntity ent [|transformTypeInfo com ctx r [||] genMap gen|] | Replacements.BclDictionary(key, value) | Replacements.FSharpMap(key, value) -> genericEntity ent [| - transformTypeInfo com ctx r genMap key - transformTypeInfo com ctx r genMap value + transformTypeInfo com ctx r [||] genMap key + transformTypeInfo com ctx r [||] genMap value |] | Replacements.FSharpResult(ok, err) -> - transformUnionReflectionInfo com ctx r ent [| - transformTypeInfo com ctx r genMap ok - transformTypeInfo com ctx r genMap err + transformUnionReflectionInfo com ctx r ent mems [| + transformTypeInfo com ctx r [||] genMap ok + transformTypeInfo com ctx r [||] genMap err |] | Replacements.FSharpChoice gen -> - let gen = List.map (transformTypeInfo com ctx r genMap) gen - List.toArray gen |> transformUnionReflectionInfo com ctx r ent + let gen = List.map (transformTypeInfo com ctx r [||] genMap) gen + List.toArray gen |> transformUnionReflectionInfo com ctx r ent mems | Replacements.FSharpReference gen -> - transformRecordReflectionInfo com ctx r ent [|transformTypeInfo com ctx r genMap gen|] + transformRecordReflectionInfo com ctx r ent mems [|transformTypeInfo com ctx r [||] genMap gen|] | _ -> - let generics = generics |> List.map (transformTypeInfo com ctx r genMap) |> List.toArray + let generics = generics |> List.map (transformTypeInfo com ctx r [||] genMap) |> List.toArray /// Check if the entity is actually declared in JS code if ent.IsInterface || FSharp2Fable.Util.isErasedEntity ent @@ -509,20 +579,24 @@ module Util = let reflectionMethodExpr = FSharp2Fable.Util.entityRefWithSuffix com ent Naming.reflectionSuffix CallExpression(com.TransformAsExpr(ctx, reflectionMethodExpr), generics) :> Expression - let transformReflectionInfo com ctx r (ent: FSharpEntity) generics = + let transformReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = if ent.IsFSharpRecord then - transformRecordReflectionInfo com ctx r ent generics + transformRecordReflectionInfo com ctx r ent mems generics elif ent.IsFSharpUnion then - transformUnionReflectionInfo com ctx r ent generics + transformUnionReflectionInfo com ctx r ent mems generics else let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression - let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; ArrayExpression generics :> Expression|] + + let members = transformMemberReflectionInfos com ctx r ent mems generics + let refl = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression + + let args = if Array.isEmpty generics then [|fullnameExpr; NullLiteral() :> Expression; refl|] else [|fullnameExpr; ArrayExpression generics :> Expression; refl|] coreLibCall com ctx None "Reflection" "type" args let transformValue (com: IBabelCompiler) (ctx: Context) r value: Expression = match value with - | Fable.TypeInfo t -> transformTypeInfo com ctx r Map.empty t + | Fable.TypeInfo t -> transformTypeInfo com ctx r [||] Map.empty t | Fable.Null _ -> upcast NullLiteral(?loc=r) | Fable.UnitConstant -> upcast NullLiteral(?loc=r) // TODO: Use `void 0`? | Fable.BoolConstant x -> upcast BooleanLiteral(x, ?loc=r) @@ -1406,7 +1480,7 @@ module Util = ExportNamedDeclaration(decl) :> ModuleDeclaration |> U2.Case2 - let declareType com ctx r isPublic (ent: FSharpEntity) name consArgs consBody baseExpr: U2 list = + let declareType com ctx r isPublic (mems : Fable.MemberInfo[]) (ent: FSharpEntity) name consArgs consBody baseExpr: U2 list = let displayName = ent.TryGetFullDisplayName() |> Option.map (Naming.unsafeReplaceIdentForbiddenChars '_') @@ -1420,7 +1494,7 @@ module Util = |> declareModuleMember isPublic name false let reflectionDeclaration = let genArgs = Array.init ent.GenericParameters.Count (fun _ -> makeIdentUnique com "gen" |> ident) - let body = transformReflectionInfo com ctx r ent (Array.map (fun x -> x :> _) genArgs) + let body = transformReflectionInfo com ctx r ent mems (Array.map (fun x -> x :> _) genArgs) makeFunctionExpression None (Array.map (fun x -> U2.Case2(upcast x)) genArgs) (U2.Case2 body) |> declareModuleMember isPublic (Naming.appendSuffix name Naming.reflectionSuffix) false [typeDeclaration; reflectionDeclaration] @@ -1498,7 +1572,7 @@ module Util = let body = [Identifier "tag" :> Expression; Identifier "name" :> _; SpreadElement(Identifier "fields") :> _] |> callFunctionWithThisContext None baseRef thisExpr |> ExpressionStatement - declareType com ctx r info.IsPublic info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) + declareType com ctx r info.IsPublic [||] info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (info: Fable.CompilerGeneratedConstructorInfo) = let args = @@ -1522,7 +1596,7 @@ module Util = then coreValue com ctx "Types" "Record" |> Some else None let args = [|for arg in args do yield arg |> toPattern|] - declareType com ctx r info.IsPublic info.Entity info.EntityName args (BlockStatement setters) baseExpr + declareType com ctx r info.IsPublic info.Members info.Entity info.EntityName args (BlockStatement setters) baseExpr let transformImplicitConstructor (com: IBabelCompiler) ctx r (info: Fable.ClassImplicitConstructorInfo) = let boundThis = Some("this", info.BoundConstructorThis) @@ -1554,7 +1628,7 @@ module Util = | None when info.Entity.IsValueType -> coreValue com ctx "Types" "Record" |> Some | None -> None [ - yield! declareType com ctx r info.IsEntityPublic info.Entity info.EntityName args body baseExpr + yield! declareType com ctx r info.IsEntityPublic info.Members info.Entity info.EntityName args body baseExpr yield declareModuleMember info.IsConstructorPublic info.Name false exposedCons ] diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 807a345c3f..21e5d02df4 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -23,6 +23,9 @@ type Helper = let argTypes = match argTypes with Some xs -> Typed xs | None -> NoUncurrying Operation(Call(kind, argInfo (Some callee) args argTypes), returnType, loc) + static member InstanceField(callee: Expr, memb: string, typ: Type, ?loc: SourceLocation) = + Get(callee, GetKind.FieldGet(memb, false, typ), typ, loc) + static member Application(callee: Expr, returnType: Type, args: Expr list, ?argTypes: Type list, ?loc: SourceLocation) = let argTypes = match argTypes with Some xs -> Typed xs | None -> NoUncurrying @@ -2585,48 +2588,85 @@ let controlExtensions (_: ICompiler) (ctx: Context) (_: SourceLocation option) t |> fun (args, argTypes) -> List.rev args, List.rev argTypes Helper.CoreCall("Observable", meth, t, args, argTypes)) -let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (_args: Expr list) = + +let callTypeInfoMethod (this : Expr) (name : string) (typ : Type) (args : list) = + // let self = com.TranslateExpr this + // let ex = Babel.MemberExpression(self, Identifier "NewInfo", computed, ?loc=r) :> Expression + Helper.InstanceCall(Helper.InstanceField(this, "NewInfo", Type.Any), name, typ, args) + +let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) = let returnString r x = StringConstant x |> makeValue r |> Some - match thisArg with - | Some(Value(TypeInfo exprType, exprRange) as thisArg) -> - match exprType with - | GenericParam name -> genericTypeInfoError name |> addError com ctx.InlinePath exprRange - | _ -> () - match i.CompiledName with - | "get_FullName" -> getTypeFullName false exprType |> returnString r - | "get_Namespace" -> - let fullname = getTypeFullName false exprType - match fullname.LastIndexOf(".") with - | -1 -> "" |> returnString r - | i -> fullname.Substring(0, i) |> returnString r - | "get_IsArray" -> - match exprType with Array _ -> true | _ -> false - |> BoolConstant |> makeValue r |> Some - | "GetElementType" -> - match exprType with - | Array t -> TypeInfo t |> makeValue r |> Some - | _ -> Null t |> makeValue r |> Some - | "get_IsGenericType" -> - List.isEmpty exprType.Generics |> not |> BoolConstant |> makeValue r |> Some - | "get_GenericTypeArguments" | "GetGenericArguments" -> - let arVals = exprType.Generics |> List.map (makeTypeInfo r) |> ArrayValues - NewArray(arVals, Any) |> makeValue r |> Some - | "GetTypeInfo" -> Some thisArg - | "GetGenericTypeDefinition" -> - let newGen = exprType.Generics |> List.map (fun _ -> Any) - exprType.ReplaceGenerics(newGen) |> TypeInfo |> makeValue exprRange |> Some - | _ -> None - | Some thisArg -> - match i.CompiledName with - | "get_GenericTypeArguments" | "GetGenericArguments" -> - Helper.CoreCall("Reflection", "getGenerics", t, [thisArg], ?loc=r) |> Some - | "get_FullName" | "get_Namespace" | "get_IsArray" | "GetElementType" - | "get_IsGenericType" | "GetGenericTypeDefinition" -> - let meth = Naming.removeGetSetPrefix i.CompiledName |> Naming.lowerFirst - Helper.CoreCall("Reflection", meth, t, [thisArg], ?loc=r) |> Some - | "GetTypeInfo" -> Some thisArg - | _ -> None - | None -> None + + + match thisArg, i.CompiledName with + | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some + | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some + | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some + | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some + | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some + | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some + | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some + | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some + | Some this, "GetTypeInfo" -> Some this + | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some + | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some + | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some + | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + + | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some + + | _ -> None + // match thisArg with + // | Some(Value(TypeInfo exprType, exprRange) as thisArg) -> + // match exprType with + // | GenericParam name -> genericTypeInfoError name |> addError com ctx.InlinePath exprRange + // | _ -> () + // match i.CompiledName with + // | "get_FullName" -> getTypeFullName false exprType |> returnString r + // | "get_Namespace" -> + // let fullname = getTypeFullName false exprType + // match fullname.LastIndexOf(".") with + // | -1 -> "" |> returnString r + // | i -> fullname.Substring(0, i) |> returnString r + // | "get_IsArray" -> + // match exprType with Array _ -> true | _ -> false + // |> BoolConstant |> makeValue r |> Some + // | "GetElementType" -> + // match exprType with + // | Array t -> TypeInfo t |> makeValue r |> Some + // | _ -> Null t |> makeValue r |> Some + // | "get_IsGenericType" -> + // List.isEmpty exprType.Generics |> not |> BoolConstant |> makeValue r |> Some + // | "get_GenericTypeArguments" | "GetGenericArguments" -> + // let arVals = exprType.Generics |> List.map (makeTypeInfo r) |> ArrayValues + // NewArray(arVals, Any) |> makeValue r |> Some + // | "GetTypeInfo" -> Some thisArg + // | "GetGenericTypeDefinition" -> + // let newGen = exprType.Generics |> List.map (fun _ -> Any) + // exprType.ReplaceGenerics(newGen) |> TypeInfo |> makeValue exprRange |> Some + // | "GetProperties" -> Helper.CoreCall("Reflection", "getTypeProperties", t, [thisArg], ?loc=r) |> Some + // | "GetFields" -> Helper.CoreCall("Reflection", "getTypeFields", t, [thisArg], ?loc=r) |> Some + // | "GetMembers" -> Helper.CoreCall("Reflection", "getTypeMembers", t, [thisArg], ?loc=r) |> Some + + // | "GetMethods" -> callTypeInfoMethod thisArg "GetMethods" t args |> Some + + // | _ -> None + // | Some thisArg -> + // match i.CompiledName with + // | "get_GenericTypeArguments" | "GetGenericArguments" -> + // Helper.CoreCall("Reflection", "getGenerics", t, [thisArg], ?loc=r) |> Some + // | "get_FullName" | "get_Namespace" | "get_IsArray" | "GetElementType" + // | "get_IsGenericType" | "GetGenericTypeDefinition" -> + // let meth = Naming.removeGetSetPrefix i.CompiledName |> Naming.lowerFirst + // Helper.CoreCall("Reflection", meth, t, [thisArg], ?loc=r) |> Some + // | "GetTypeInfo" -> Some thisArg + // | "GetProperties" -> Helper.CoreCall("Reflection", "getTypeProperties", t, [thisArg], ?loc=r) |> Some + // | "GetFields" -> Helper.CoreCall("Reflection", "getTypeFields", t, [thisArg], ?loc=r) |> Some + // | "GetMembers" -> Helper.CoreCall("Reflection", "getTypeMembers", t, [thisArg], ?loc=r) |> Some + + // | "GetMethods" -> callTypeInfoMethod thisArg "GetMethods" t args |> Some + // | _ -> None + // | None -> None let fsharpType methName (r: SourceLocation option) t (i: CallInfo) (args: Expr list) = match methName with @@ -2815,10 +2855,28 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr if isFSharpType then fsharpType methName r t info args else fsharpValue methName r t info args - | "Microsoft.FSharp.Reflection.UnionCaseInfo" + | "System.Reflection.MemberInfo" + | "System.Reflection.MethodBase" | "System.Reflection.PropertyInfo" - | "System.Reflection.MemberInfo" -> + | "System.Reflection.FieldInfo" + | "System.Reflection.MethodInfo" -> + match thisArg, info.CompiledName, args with + | Some c, "Invoke", _ -> Helper.InstanceCall(c, "Invoke", t, args) |> Some + | Some c, "get_Name", [] -> Helper.InstanceCall(c, "get_Name", t, []) |> Some + | Some c, "get_DeclaringType", [] -> Helper.InstanceCall(c, "get_DeclaringType", t, []) |> Some + | Some c, "GetCustomAttributes", args -> Helper.InstanceCall(c, "GetCustomAttributes", t, args) |> Some + | Some c, "GetParameters", [] -> Helper.InstanceCall(c, "GetParameters", t, []) |> Some + | Some c, "get_IsStatic", [] -> Helper.InstanceCall(c, "get_IsStatic", t, []) |> Some + | Some c, "get_IsGenericMethod", [] -> Helper.InstanceCall(c, "get_IsGenericMethod", t, []) |> Some + | Some c, "GetGenericMethodDefinition", [] -> Helper.InstanceCall(c, "GetGenericMethodDefinition", t, []) |> Some + | Some c, "get_ReturnType", [] -> Helper.InstanceCall(c, "get_ReturnType", t, []) |> Some + | Some c, "get_ReturnParameter", [] -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some + | _ -> None + + | "Microsoft.FSharp.Reflection.UnionCaseInfo" -> match thisArg, info.CompiledName with + | Some c, "GetCustomAttributes" -> Helper.CoreCall("Reflection", "customAttributes", t, [c], ?loc=r) |> Some + | Some c, "get_IsStatic" -> Helper.CoreCall("Reflection", "isStatic", t, [c], ?loc=r) |> Some | Some c, "get_Tag" -> makeStrConst "tag" |> getExpr r t c |> Some | Some c, "get_PropertyType" -> makeIntConst 1 |> getExpr r t c |> Some | Some c, "GetFields" -> Helper.CoreCall("Reflection", "getUnionCaseFields", t, [c], ?loc=r) |> Some @@ -2826,7 +2884,7 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr match c with | Value(TypeInfo exprType, loc) -> match exprType with - | GenericParam name -> genericTypeInfoError name |> addError com ctx.InlinePath loc + | GenericParam name -> genericTypeInfoError name |> addWarning com ctx.InlinePath loc | _ -> () let fullname = getTypeFullName false exprType diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 5349673013..4f8205d7b5 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -69,6 +69,7 @@ type ExprConstInfo = | LetRecCombOp | ValueOp of obj * System.Type * Option | IfThenElseOp + | NewRecordOp of System.Type and Tree = | CombTerm of ExprConstInfo * Expr list @@ -99,7 +100,8 @@ and [ typeOf b2 | LetRecOp, _ -> failwith "bad" | LetRecCombOp, _ -> failwith "bad" - + + | NewRecordOp t, _ -> t member internal x.Tree = tree @@ -148,6 +150,9 @@ and [) = + Expr(CombTerm(NewRecordOp t, args)) + [] module Patterns = @@ -201,4 +206,9 @@ module Patterns = | E(CombTerm(IfThenElseOp, [c; i; e])) -> Some (c,i,e) | _ -> None - \ No newline at end of file + + [] + let (|NewRecord|_|) (e : Expr) = + match e with + | E(CombTerm(NewRecordOp t, args)) -> Some (t, args) + | _ -> None \ No newline at end of file diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 3fd7caff26..39b61dc3da 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1,7 +1,372 @@ import { Record, Union } from "./Types"; import { compareArraysWith, equalArraysWith } from "./Util"; -export type FieldInfo = [string, TypeInfo]; +// tslint:disable: max-line-length + +export enum BindingFlags { + Instance = 4, + NonPublic = 32, + Public = 16, + Static = 8, +} + +export class NParameterInfo { + constructor( + public Name: string, + public ParameterType: NTypeInfo, + ) {} + + public toString() { + return this.Name + " : " + this.ParameterType.toShortString(); + } +} + +export class NMemberInfo { + constructor( + public DeclaringType: NTypeInfo, + public Name: string, + private attributes: CustomAttribute[], + ) {} + + public get_Name() { return this.Name; } + + public get_DeclaringType() { return this.DeclaringType; } + + public GetCustomAttributes(a: (boolean|NTypeInfo), b?: boolean) { + if (typeof a === "boolean") { + return this.attributes; + } else if (a.fullname) { + return this.attributes.filter((att) => att.AttributeType === a.fullname).map((att) => att.AttributeValue); + } else { + return this.attributes.map((att) => att.AttributeValue); + } + } +} + +export class NMethodBase extends NMemberInfo { + constructor( + DeclaringType: NTypeInfo, + Name: string, + public Parameters: NParameterInfo[], + public IsStatic: boolean, + private invoke: (...args: any[]) => any, + attributes: CustomAttribute[], + ) { + super(DeclaringType, Name, attributes); + } + + public GetParameters() { return this.Parameters; } + + public get_IsStatic() { return this.IsStatic; } + + public get_IsGenericMethod() { return false; } + + public GetGenericMethodDefinition() { return this; } + + public Invoke(target: any, ...args: any[]) { + if (!this.IsStatic) { + const a = [target, ...args]; + return this.invoke.apply(null, [target, ...args]); + } else { + return this.invoke.apply(null, args); + } + } +} + +export class NMethodInfo extends NMethodBase { + constructor( + DeclaringType: NTypeInfo, + Name: string, + Parameters: NParameterInfo[], + public ReturnType: NTypeInfo, + IsStatic: boolean, + invoke: (...args: any[]) => any, + attributes: CustomAttribute[], + ) { + super(DeclaringType, Name, Parameters, IsStatic, invoke, attributes); + } + + public get_ReturnType() { return this.ReturnType; } + + public get_ReturnParameter() { + return new NParameterInfo("Return", this.ReturnType); + } + + public toString() { + const args = this.Parameters.map((p) => p.toString()).join(", "); + + let attPrefix = ""; + const atts = this.GetCustomAttributes(true); + if (atts.length > 0) { + attPrefix = "[<" + atts.map((a) => a.toString()).join("; ") + ">] "; + } + let prefix = "member "; + if (this.IsStatic) { prefix = "static " + prefix; } + + return attPrefix + prefix + this.Name + "(" + args + ") : " + this.ReturnType.toShortString(); + } +} + +export class NConstructorInfo extends NMethodBase { + constructor( + DeclaringType: NTypeInfo, + Name: string, + Parameters: NParameterInfo[], + IsStatic: boolean, + invoke: (...args: any[]) => any, + attributes: CustomAttribute[], + ) { + super(DeclaringType, Name, Parameters, IsStatic, invoke, attributes); + } + + public toString() { + const args = this.Parameters.map((p) => p.toString()).join(", "); + + let attPrefix = ""; + const atts = this.GetCustomAttributes(true); + if (atts.length > 0) { + attPrefix = "[<" + atts.map((a) => a.toString()).join("; ") + ">] "; + } + + return attPrefix + "new(" + args + ")"; + } +} + +export class NFieldInfo extends NMemberInfo { + constructor( + DeclaringType: NTypeInfo, + Name: string, + public Type: NTypeInfo, + public IsStatic: boolean, + attributes: CustomAttribute[], + ) { + super(DeclaringType, Name, attributes); + } + public get_FieldType() { return this.Type; } + public get_IsStatic() { return this.IsStatic; } + + public toString() { + const typ = this.Type.toShortString(); + let prefix = "val "; + if (this.IsStatic) { prefix = "static " + prefix; } + + let attPrefix = ""; + const atts = this.GetCustomAttributes(true); + if (atts.length > 0) { + attPrefix = "[<" + atts.map((a) => a.toString()).join("; ") + ">] "; + } + + return attPrefix + prefix + this.Name + " : " + typ; + } +} + +export class NPropertyInfo extends NMemberInfo { + constructor( + DeclaringType: NTypeInfo, + Name: string, + public Type: NTypeInfo, + public IsStatic: boolean, + public IsFSharp: boolean, + attributes: CustomAttribute[], + ) { + super(DeclaringType, Name, attributes); + } + public get_PropertyType() { return this.Type; } + public get_IsStatic() { return this.IsStatic; } + public get_IsFSharp() { return this.IsFSharp; } + + public get_GetMethod() { + const getterName = "get_" + this.Name; + const mems = this.DeclaringType.GetMembers(); + const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); + if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } + } + + public get_SetMethod() { + const getterName = "set_" + this.Name; + const mems = this.DeclaringType.GetMembers(); + const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); + if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } + } + + public GetValue(target: any, ...index: any[]) { + const g = this.get_GetMethod(); + return g.Invoke(target, index); + } + + public SetValue(target: any, value: any, ...index: any[]) { + const s = this.get_SetMethod(); + s.Invoke(target, [...index, value]); + } + + public toString() { + const g = this.get_GetMethod(); + const s = this.get_SetMethod(); + let typ = this.Type.toShortString(); + if (g && g.Parameters.length > 0) { + const prefix = g.Parameters.map((p) => p.ParameterType.toShortString()).join(" * "); + typ = prefix + " -> " + typ; + } else if (s && s.Parameters.length > 0) { + const prefix = s.Parameters.slice(0, s.Parameters.length - 1).map((p) => p.ParameterType.toShortString()).join(" * "); + typ = prefix + " -> " + typ; + } + + let suffix = ""; + if (g && s) { + suffix = " with get, set"; + } else if (g) { + suffix = " with get"; + } else if (s) { + suffix = " with set"; + } + + let prefix = "member "; + if (this.IsStatic) { prefix = "static " + prefix; } + + let attPrefix = ""; + const atts = this.GetCustomAttributes(true); + if (atts.length > 0) { + attPrefix = "[<" + atts.map((a) => a.toString()).join("; ") + ">] "; + } + + return attPrefix + prefix + this.Name + " : " + typ + suffix; + } +} + +export class NTypeInfo { + public static getParameter(i: string) { + if (NTypeInfo.parameterCache[i]) { + return NTypeInfo.parameterCache[i]; + } else { + const p = new NTypeInfo(i, 0, true, [], (_s) => [], () => []); + this.parameterCache[i] = p; + return p; + } + } + private static parameterCache: {[i: string]: NTypeInfo} = {}; + + private mems: NMemberInfo[] = null; + + constructor( + public fullname: string, + public genericCount: number, + public isGenericParameter: boolean, + public generics: NTypeInfo[], + public members: (self: NTypeInfo) => NMemberInfo[], + public cases: () => CaseInfo[]) {} + + public GetGenericTypeDefinition() { + if (this.genericCount === 0 || this.generics.length === 0) { + return this; + } else { + return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members, this.cases); + } + } + + public MakeGenericType(args: NTypeInfo[]) { + if (args.length !== this.genericCount) { throw new Error("invalid generic argument count "); } + return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this.cases); + } + + public get_FullName() { + return this.fullname; + } + public get_Namespace() { + const i = this.fullname.lastIndexOf("."); + return i === -1 ? "" : this.fullname.substr(0, i); + } + public get_IsArray() { + return this.fullname.endsWith("[]"); + } + + public GetElementType(): NTypeInfo { + return this.get_IsArray() ? this.generics[0] : null; + } + + public get_IsGenericType() { + return this.genericCount > 0; + } + public get_IsGenericTypeDefinition() { + return this.genericCount > 0 && !this.generics; + } + + public GetGenericArguments() { + if (this.genericCount > 0) { + if (this.generics) { + return this.generics; + } else { + return Array(this.genericCount).map((_, i) => NTypeInfo.getParameter(i.toString())); + } + } else { + return []; + } + } + + public get_GenericTypeArguments() { + return this.GetGenericArguments(); + } + + public GetMembers() { + if (!this.mems) { + if (this.members) { + this.mems = this.members(this); + } else { + this.mems = []; + } + } + return this.mems; + } + + public GetProperties() { + const m = this.GetMembers(); + return m.filter((m) => m instanceof NPropertyInfo) as NPropertyInfo[]; + } + + public GetMethods() { + const m = this.GetMembers(); + return m.filter((m) => m instanceof NMethodInfo) as NMethodInfo[]; + } + + public GetProperty(name: string) { + const m = this.GetMembers(); + const prop = m.find((m) => m instanceof NPropertyInfo && m.Name === name) as NPropertyInfo; + return prop; + } + public GetMethod(name: string) { + const m = this.GetMembers(); + const meth = m.find((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo; + return meth; + } + + public toShortString() { + return this.fullname; + } + public toString() { + const members = + this + .GetMembers() + .filter((m) => !(m instanceof NMethodInfo) || !(m.Name.startsWith("get_") || m.Name.startsWith("set_"))) + .map((m) => " " + m.toString()).join("\n"); + return "type " + this.fullname + "=\n" + members; + } +} + +export enum MemberKind { + Property = 0, + Field = 1, + FSharpField = 2, + Constructor = 3, + Method = 4, +} + +export type ParameterInfo = [string, TypeInfo]; + +export type FieldInfo = [string, TypeInfo, boolean, MemberKind, ParameterInfo[], CustomAttribute[], (...args: any) => any]; + +export interface CustomAttribute { + AttributeType: string; + AttributeValue: any; +} export type Constructor = new(...args: any[]) => any; @@ -13,12 +378,85 @@ export class CaseInfo { } } +const typeCache: { [fullname: string]: NTypeInfo } = {}; + +function mkParameterInfo(info: ParameterInfo) { + return new NParameterInfo(info[0], mkNTypeInfo(info[1])); +} + +function mkNMemberInfo(self: NTypeInfo, info: FieldInfo): NMemberInfo { + switch (info[3]) { + case MemberKind.Method: + return new NMethodInfo( + self, info[0], info[4].map((a) => mkParameterInfo(a)), + mkNTypeInfo(info[1]), info[2], info[6], info[5], + ); + case MemberKind.Property: + return new NPropertyInfo(self, info[0], mkNTypeInfo(info[1]), info[2], false, info[5]); + case MemberKind.FSharpField: + return new NPropertyInfo(self, info[0], mkNTypeInfo(info[1]), info[2], true, info[5]); + case MemberKind.Constructor: + return new NConstructorInfo( + self, info[0], info[4].map((a) => mkParameterInfo(a)), + info[2], info[6], info[5], + ); + case MemberKind.Field: + return new NFieldInfo(self, info[0], mkNTypeInfo(info[1]), info[2], info[5]); + default: + return null; + } +} +function mkNMemberInfos(fields: () => FieldInfo[]): (self: NTypeInfo) => NMemberInfo[] { + if (fields) { + return (self: NTypeInfo) => fields().map((f) => mkNMemberInfo(self, f)).filter((f) => f !== null); + } else { + return (_self: NTypeInfo) => []; + } +} + +function mkNTypeInfo(info: TypeInfo): NTypeInfo { + if (typeCache[info.fullname]) { + return typeCache[info.fullname]; + } else { + const gen = info.generics || []; + const cs = info.cases || (() => []); + const fields = info.fields || (() => []); + + const res = new NTypeInfo(info.fullname, gen.length, false, gen.map((a) => mkNTypeInfo(a)), mkNMemberInfos(fields), cs); + typeCache[info.fullname] = res; + return res; + } +} + +export function declareType(fullname: string, generics: NTypeInfo[], members: () => FieldInfo[], cases: () => CaseInfo[]): NTypeInfo { + let gen: NTypeInfo = null; + if (typeCache[fullname]) { + gen = typeCache[fullname]; + } else { + gen = new NTypeInfo(fullname, generics.length, false, [], mkNMemberInfos(members), cases); + typeCache[fullname] = gen; + } + + if (generics.length > 0 && generics.length === gen.genericCount) { + return gen.MakeGenericType(generics); + } else { + return gen; + } +} + +export function getGenericParamter(name: string) { + NTypeInfo.getParameter(name); +} + + export class TypeInfo { + public NewInfo: NTypeInfo; constructor(public fullname: string, public generics?: TypeInfo[], public constructor?: Constructor, public fields?: () => FieldInfo[], public cases?: () => CaseInfo[]) { + this.NewInfo = mkNTypeInfo(this); } public toString() { return fullName(this); @@ -31,11 +469,11 @@ export class TypeInfo { } } -export function getGenerics(t: TypeInfo): TypeInfo[] { +export function getGenerics(t: (TypeInfo|NTypeInfo)): Array<(TypeInfo|NTypeInfo)> { return t.generics != null ? t.generics : []; } -export function equals(t1: TypeInfo, t2: TypeInfo): boolean { +export function equals(t1: (TypeInfo|NTypeInfo), t2: (TypeInfo|NTypeInfo)): boolean { return t1.fullname === t2.fullname && equalArraysWith(getGenerics(t1), getGenerics(t2), equals); } @@ -50,8 +488,10 @@ export function compare(t1: TypeInfo, t2: TypeInfo): number { } } -export function type(fullname: string, generics?: TypeInfo[]): TypeInfo { - return new TypeInfo(fullname, generics); +export function type(fullname: string, generics?: TypeInfo[], fields?: () => FieldInfo[]): TypeInfo { + const gen = generics || []; + const f = fields || (() => []); + return new TypeInfo(fullname, gen, null, f); } export function record(fullname: string, generics: TypeInfo[], @@ -59,6 +499,10 @@ export function record(fullname: string, generics: TypeInfo[], return new TypeInfo(fullname, generics, constructor, fields); } +export function customAttributes(info: FieldInfo): CustomAttribute[] { + return info[5]; +} + export type CaseInfoInput = string | [string, TypeInfo[]]; export function union(fullname: string, generics: TypeInfo[], @@ -164,7 +608,7 @@ export function getUnionCases(t: TypeInfo): CaseInfo[] { export function getRecordElements(t: TypeInfo): FieldInfo[] { if (t.fields != null) { - return t.fields(); + return t.fields().filter((arr) => arr[3] === MemberKind.FSharpField); } else { throw new Error(`${t.fullname} is not an F# record type`); } @@ -187,6 +631,36 @@ export function getFunctionElements(t: TypeInfo): [TypeInfo, TypeInfo] { } } +export function getTypeProperties(t: TypeInfo): FieldInfo[] { + if (t.fields) { + return t.fields() + .filter((arr) => arr[3] === MemberKind.Property || arr[3] === MemberKind.FSharpField); + } else { + return []; + } +} + +export function isStatic(i: FieldInfo) { + return i[2]; +} + +export function getTypeFields(t: TypeInfo): FieldInfo[] { + if (t.fields) { + return t.fields() + .filter((arr) => arr[3] === MemberKind.Field); + } else { + return []; + } +} + +export function getTypeMembers(t: TypeInfo): FieldInfo[] { + if (t.fields) { + return t.fields(); + } else { + return []; + } +} + export function isUnion(t: any): boolean { return t instanceof TypeInfo ? t.cases != null : t instanceof Union; } @@ -216,7 +690,7 @@ export function getUnionFields(v: any, t: TypeInfo): [CaseInfo, any[]] { } export function getUnionCaseFields(uci: CaseInfo): FieldInfo[] { - return uci.fields == null ? [] : uci.fields.map((t, i) => ["Data" + i, t] as FieldInfo); + return uci.fields == null ? [] : uci.fields.map(( t, i ) => ["Data" + i, t, false, MemberKind.FSharpField, [], [], null] as FieldInfo); } export function getRecordFields(v: any): any[] { diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index c3e40ae357..6518d14cc2 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -18,11 +18,52 @@ type SeppBuilder() = let sepp = SeppBuilder() +type BlaAttribute(name : string) = + inherit Attribute() + member x.Name = name + override x.ToString() = sprintf "Bla(%s)" name + +type BlubbAttribute(name : string) = + inherit Attribute() + member x.Name = name + override x.ToString() = sprintf "Blubb(%s)" name + type V2 = { x : int; y : int } with + [] + static member Blubber = { x = 1; y = 3} + + [] + member x.Sepp = + x.x + x.y + static member (+) (l : V2, r : V2) = { x = l.x + r.x; y = l.y + r.y } + member x.Item + with set (i : int) (v : int) = + () + open FSharp.Quotations open FSharp.Quotations.Patterns +open Fable.Core + +type System.Type with + [] + member x.NewInfo : obj = jsNative + +type System.Reflection.MemberInfo with + + [] + member x.Invoke1 (v : 'a) : 'b = Util.jsNative + + [] + member x.Invoke0 () : 'b = Util.jsNative + + [] + member x.IsStatic : bool = Util.jsNative + +type Sepp(a : int, b : string) = + member x.Yeah = b + member x.DoIt(c : int) = a*c let tests = testList "Expr" [ @@ -121,7 +162,16 @@ let tests = equal b.Type typeof | _ -> failwith "bad recursive binding" - - - + testCase "Expr.NewRecord" <| fun () -> + let e = Expr.NewRecord(typeof, [Expr.Value 10; Expr.Value 1]) + match e with + | NewRecord(t, [a;b]) -> + equal t typeof + | _ -> + failwith "bad record" + + testCase "CustomAttributes" <| fun () -> + + let prop = typeof.GetMembers() //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) + failwithf "prop: %A" prop ] \ No newline at end of file From 5f300b633ae18b621d51ae9268970c154180a043 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sat, 6 Apr 2019 19:20:42 +0200 Subject: [PATCH 04/38] removed old Reflection API --- src/Fable.Transforms/AST/AST.Fable.fs | 1 + src/Fable.Transforms/FSharp2Fable.fs | 14 +- src/Fable.Transforms/Fable2Babel.fs | 106 +++--- src/Fable.Transforms/Replacements.fs | 86 ++--- src/fable-library/Reflection.ts | 482 ++++++++++++++------------ 5 files changed, 368 insertions(+), 321 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 729114fc36..35573070fd 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -67,6 +67,7 @@ type MemberInfoKind = | Field of name : string * typ : Type * isStatic : bool | Method of name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string | Constructor of parameters : ParameterInfo[] * mangledName : string + | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string type MemberInfo = { diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index f8def57d34..9255abf153 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -898,11 +898,15 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar ] elif ent.IsFSharpUnion then - // let caseInfos = - // ent.UnionCases |> Seq.collect (fun c -> - // Seq.empty - // ) - Seq.empty + ent.UnionCases |> Seq.mapi (fun i c -> + let mangledName = Helpers.unionCaseCompiledName c |> Option.defaultValue c.Name + + let fields = c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> f.Name, makeType com ctx.GenericArgs f.FieldType) + { + Fable.MemberInfo.Kind = Fable.UnionCaseConstructor(i, c.Name, fields, mangledName) + Fable.Attributes = c.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + ) else Seq.empty diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index adbcf04da2..26b745e24e 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -380,6 +380,24 @@ module Util = )) match x.Kind with + | Fable.MemberInfoKind.UnionCaseConstructor(tag, name, pars, mangledName) -> + let parInfos = pars |> Array.map (fun (pn, pt) -> ArrayExpression [| StringLiteral pn; transformTypeInfo com ctx r [||] genMap pt |] :> Expression) + let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit + + let invoke = + let args = pars |> Array.map (fun (pn, pt) -> Identifier(pn)) + let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) + + let res = + ArrayExpression [| + StringLiteral name; ret; NumericLiteral (float tag); NumericLiteral(5.0); + ArrayExpression parInfos; + attributes + invoke + |] :> Expression + Some res | Fable.MemberInfoKind.Constructor(pars, mangledName) -> let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit @@ -450,34 +468,35 @@ module Util = let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray Array.zip genParamNames generics |> Map let members = transformMemberReflectionInfos com ctx r ent mems generics - let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression - [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; fields|] - |> coreLibCall com ctx None "Reflection" "record" - - and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = - let fullname = defaultArg ent.TryFullName Naming.unknown - let fullnameExpr = StringLiteral fullname :> Expression - let genMap = - let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - Array.zip genParamNames generics |> Map - let cases = - ent.UnionCases |> Seq.map (fun uci -> - let fieldTypes = - uci.UnionCaseFields |> Seq.map (fun fi -> - FSharp2Fable.TypeHelpers.makeType com Map.empty fi.FieldType - |> transformTypeInfo com ctx r mems genMap) |> Seq.toArray - let caseInfo = - if fieldTypes.Length = 0 then - getUnionCaseName uci |> StringLiteral :> Expression - else - ArrayExpression [| - getUnionCaseName uci |> StringLiteral :> Expression - ArrayExpression fieldTypes :> Expression - |] :> Expression - caseInfo) |> Seq.toArray - let cases = ArrowFunctionExpression([||], ArrayExpression cases :> Expression |> U2.Case2) :> Expression - [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; cases|] - |> coreLibCall com ctx None "Reflection" "union" + //let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression + let fields = FunctionExpression([||], BlockStatement [| ReturnStatement(ArrayExpression members) :> Statement |]) :> Expression + [|fullnameExpr; upcast ArrayExpression generics; fields|] + |> coreLibCall com ctx None "Reflection" "type" + + // and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = + // let fullname = defaultArg ent.TryFullName Naming.unknown + // let fullnameExpr = StringLiteral fullname :> Expression + // let genMap = + // let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray + // Array.zip genParamNames generics |> Map + // let cases = + // ent.UnionCases |> Seq.map (fun uci -> + // let fieldTypes = + // uci.UnionCaseFields |> Seq.map (fun fi -> + // FSharp2Fable.TypeHelpers.makeType com Map.empty fi.FieldType + // |> transformTypeInfo com ctx r mems genMap) |> Seq.toArray + // let caseInfo = + // if fieldTypes.Length = 0 then + // getUnionCaseName uci |> StringLiteral :> Expression + // else + // ArrayExpression [| + // getUnionCaseName uci |> StringLiteral :> Expression + // ArrayExpression fieldTypes :> Expression + // |] :> Expression + // caseInfo) |> Seq.toArray + // let cases = ArrowFunctionExpression([||], ArrayExpression cases :> Expression |> U2.Case2) :> Expression + // [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; cases|] + // |> coreLibCall com ctx None "Reflection" "union" and transformTypeInfo (com: IBabelCompiler) ctx r (mems : Fable.MemberInfo[]) (genMap: Map) t: Expression = let error msg = @@ -558,13 +577,13 @@ module Util = transformTypeInfo com ctx r [||] genMap value |] | Replacements.FSharpResult(ok, err) -> - transformUnionReflectionInfo com ctx r ent mems [| + transformRecordReflectionInfo com ctx r ent mems [| transformTypeInfo com ctx r [||] genMap ok transformTypeInfo com ctx r [||] genMap err |] | Replacements.FSharpChoice gen -> let gen = List.map (transformTypeInfo com ctx r [||] genMap) gen - List.toArray gen |> transformUnionReflectionInfo com ctx r ent mems + List.toArray gen |> transformRecordReflectionInfo com ctx r ent mems | Replacements.FSharpReference gen -> transformRecordReflectionInfo com ctx r ent mems [|transformTypeInfo com ctx r [||] genMap gen|] | _ -> @@ -580,19 +599,20 @@ module Util = CallExpression(com.TransformAsExpr(ctx, reflectionMethodExpr), generics) :> Expression let transformReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = - if ent.IsFSharpRecord then - transformRecordReflectionInfo com ctx r ent mems generics - elif ent.IsFSharpUnion then - transformUnionReflectionInfo com ctx r ent mems generics - else - let fullname = defaultArg ent.TryFullName Naming.unknown - let fullnameExpr = StringLiteral fullname :> Expression - - let members = transformMemberReflectionInfos com ctx r ent mems generics - let refl = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression - - let args = if Array.isEmpty generics then [|fullnameExpr; NullLiteral() :> Expression; refl|] else [|fullnameExpr; ArrayExpression generics :> Expression; refl|] - coreLibCall com ctx None "Reflection" "type" args + transformRecordReflectionInfo com ctx r ent mems generics + // if ent.IsFSharpRecord then + // transformRecordReflectionInfo com ctx r ent mems generics + // elif ent.IsFSharpUnion then + // transformRecordReflectionInfo com ctx r ent mems generics + // else + // let fullname = defaultArg ent.TryFullName Naming.unknown + // let fullnameExpr = StringLiteral fullname :> Expression + + // let members = transformMemberReflectionInfos com ctx r ent mems generics + // let refl = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression + + // let args = if Array.isEmpty generics then [|fullnameExpr; NullLiteral() :> Expression; refl|] else [|fullnameExpr; ArrayExpression generics :> Expression; refl|] + // coreLibCall com ctx None "Reflection" "type" args let transformValue (com: IBabelCompiler) (ctx: Context) r value: Expression = match value with diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 21e5d02df4..8f47b82cfa 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2592,28 +2592,28 @@ let controlExtensions (_: ICompiler) (ctx: Context) (_: SourceLocation option) t let callTypeInfoMethod (this : Expr) (name : string) (typ : Type) (args : list) = // let self = com.TranslateExpr this // let ex = Babel.MemberExpression(self, Identifier "NewInfo", computed, ?loc=r) :> Expression - Helper.InstanceCall(Helper.InstanceField(this, "NewInfo", Type.Any), name, typ, args) - + //Helper.InstanceCall(Helper.InstanceField(this, "NewInfo", Type.Any), name, typ, args) + Helper.InstanceCall(this, name, typ, args) let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) = let returnString r x = StringConstant x |> makeValue r |> Some match thisArg, i.CompiledName with - | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some - | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some - | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some - | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some - | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some - | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some - | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some - | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some - | Some this, "GetTypeInfo" -> Some this - | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some - | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some - | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some - | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some - - | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some + | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some + | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some + | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some + | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some + | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some + | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some + | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some + | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some + | Some this, "GetTypeInfo" -> Some this + | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some + | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some + | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some + | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + + | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some | _ -> None // match thisArg with @@ -2859,6 +2859,7 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | "System.Reflection.MethodBase" | "System.Reflection.PropertyInfo" | "System.Reflection.FieldInfo" + | "Microsoft.FSharp.Reflection.UnionCaseInfo" | "System.Reflection.MethodInfo" -> match thisArg, info.CompiledName, args with | Some c, "Invoke", _ -> Helper.InstanceCall(c, "Invoke", t, args) |> Some @@ -2871,33 +2872,34 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | Some c, "GetGenericMethodDefinition", [] -> Helper.InstanceCall(c, "GetGenericMethodDefinition", t, []) |> Some | Some c, "get_ReturnType", [] -> Helper.InstanceCall(c, "get_ReturnType", t, []) |> Some | Some c, "get_ReturnParameter", [] -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some + | Some c, "GetFields", [] -> Helper.InstanceCall(c, "GetFields", t, []) |> Some | _ -> None - | "Microsoft.FSharp.Reflection.UnionCaseInfo" -> - match thisArg, info.CompiledName with - | Some c, "GetCustomAttributes" -> Helper.CoreCall("Reflection", "customAttributes", t, [c], ?loc=r) |> Some - | Some c, "get_IsStatic" -> Helper.CoreCall("Reflection", "isStatic", t, [c], ?loc=r) |> Some - | Some c, "get_Tag" -> makeStrConst "tag" |> getExpr r t c |> Some - | Some c, "get_PropertyType" -> makeIntConst 1 |> getExpr r t c |> Some - | Some c, "GetFields" -> Helper.CoreCall("Reflection", "getUnionCaseFields", t, [c], ?loc=r) |> Some - | Some c, "get_Name" -> - match c with - | Value(TypeInfo exprType, loc) -> - match exprType with - | GenericParam name -> genericTypeInfoError name |> addWarning com ctx.InlinePath loc - | _ -> () - - let fullname = getTypeFullName false exprType - let fullname = - match fullname.IndexOf("[") with - | -1 -> fullname - | i -> fullname.[..i - 1] - match fullname.LastIndexOf(".") with - | -1 -> fullname |> StringConstant |> makeValue r |> Some - | i -> fullname.Substring(i + 1) |> StringConstant |> makeValue r |> Some - | c -> - Helper.CoreCall("Reflection", "name", t, [c], ?loc=r) |> Some - | _ -> None + // | "Microsoft.FSharp.Reflection.UnionCaseInfo" -> + // match thisArg, info.CompiledName with + // | Some c, "GetCustomAttributes" -> Helper.CoreCall("Reflection", "customAttributes", t, [c], ?loc=r) |> Some + // | Some c, "get_IsStatic" -> Helper.CoreCall("Reflection", "isStatic", t, [c], ?loc=r) |> Some + // | Some c, "get_Tag" -> makeStrConst "tag" |> getExpr r t c |> Some + // | Some c, "get_PropertyType" -> makeIntConst 1 |> getExpr r t c |> Some + // | Some c, "GetFields" -> Helper.CoreCall("Reflection", "getUnionCaseFields", t, [c], ?loc=r) |> Some + // | Some c, "get_Name" -> + // match c with + // | Value(TypeInfo exprType, loc) -> + // match exprType with + // | GenericParam name -> genericTypeInfoError name |> addWarning com ctx.InlinePath loc + // | _ -> () + + // let fullname = getTypeFullName false exprType + // let fullname = + // match fullname.IndexOf("[") with + // | -1 -> fullname + // | i -> fullname.[..i - 1] + // match fullname.LastIndexOf(".") with + // | -1 -> fullname |> StringConstant |> makeValue r |> Some + // | i -> fullname.Substring(i + 1) |> StringConstant |> makeValue r |> Some + // | c -> + // Helper.CoreCall("Reflection", "name", t, [c], ?loc=r) |> Some + // | _ -> None | _ when not info.IsInterface -> com.Options.precompiledLib |> Option.bind (fun tryLib -> tryLib info.DeclaringEntityFullName) diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 39b61dc3da..4b57c576fc 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1,15 +1,8 @@ import { Record, Union } from "./Types"; -import { compareArraysWith, equalArraysWith } from "./Util"; +import { compareArraysWith, equalArraysWith, stringHash } from "./Util"; // tslint:disable: max-line-length -export enum BindingFlags { - Instance = 4, - NonPublic = 32, - Public = 16, - Static = 8, -} - export class NParameterInfo { constructor( public Name: string, @@ -17,7 +10,7 @@ export class NParameterInfo { ) {} public toString() { - return this.Name + " : " + this.ParameterType.toShortString(); + return this.Name + " : " + this.ParameterType.toString(); } } @@ -49,7 +42,6 @@ export class NMethodBase extends NMemberInfo { Name: string, public Parameters: NParameterInfo[], public IsStatic: boolean, - private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); @@ -63,14 +55,6 @@ export class NMethodBase extends NMemberInfo { public GetGenericMethodDefinition() { return this; } - public Invoke(target: any, ...args: any[]) { - if (!this.IsStatic) { - const a = [target, ...args]; - return this.invoke.apply(null, [target, ...args]); - } else { - return this.invoke.apply(null, args); - } - } } export class NMethodInfo extends NMethodBase { @@ -80,10 +64,10 @@ export class NMethodInfo extends NMethodBase { Parameters: NParameterInfo[], public ReturnType: NTypeInfo, IsStatic: boolean, - invoke: (...args: any[]) => any, + private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { - super(DeclaringType, Name, Parameters, IsStatic, invoke, attributes); + super(DeclaringType, Name, Parameters, IsStatic, attributes); } public get_ReturnType() { return this.ReturnType; } @@ -103,7 +87,16 @@ export class NMethodInfo extends NMethodBase { let prefix = "member "; if (this.IsStatic) { prefix = "static " + prefix; } - return attPrefix + prefix + this.Name + "(" + args + ") : " + this.ReturnType.toShortString(); + return attPrefix + prefix + this.Name + "(" + args + ") : " + this.ReturnType.toString(); + } + + public Invoke(target: any, ...args: any[]) { + if (!this.IsStatic) { + const a = [target, ...args]; + return this.invoke.apply(null, [target, ...args]); + } else { + return this.invoke.apply(null, args); + } } } @@ -113,10 +106,10 @@ export class NConstructorInfo extends NMethodBase { Name: string, Parameters: NParameterInfo[], IsStatic: boolean, - invoke: (...args: any[]) => any, + private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { - super(DeclaringType, Name, Parameters, IsStatic, invoke, attributes); + super(DeclaringType, Name, Parameters, IsStatic, attributes); } public toString() { @@ -130,6 +123,10 @@ export class NConstructorInfo extends NMethodBase { return attPrefix + "new(" + args + ")"; } + + public Invoke(...args: any[]) { + return this.invoke.apply(null, args); + } } export class NFieldInfo extends NMemberInfo { @@ -146,7 +143,7 @@ export class NFieldInfo extends NMemberInfo { public get_IsStatic() { return this.IsStatic; } public toString() { - const typ = this.Type.toShortString(); + const typ = this.Type.toString(); let prefix = "val "; if (this.IsStatic) { prefix = "static " + prefix; } @@ -202,12 +199,12 @@ export class NPropertyInfo extends NMemberInfo { public toString() { const g = this.get_GetMethod(); const s = this.get_SetMethod(); - let typ = this.Type.toShortString(); + let typ = this.Type.toString(); if (g && g.Parameters.length > 0) { - const prefix = g.Parameters.map((p) => p.ParameterType.toShortString()).join(" * "); + const prefix = g.Parameters.map((p) => p.ParameterType.toString()).join(" * "); typ = prefix + " -> " + typ; } else if (s && s.Parameters.length > 0) { - const prefix = s.Parameters.slice(0, s.Parameters.length - 1).map((p) => p.ParameterType.toShortString()).join(" * "); + const prefix = s.Parameters.slice(0, s.Parameters.length - 1).map((p) => p.ParameterType.toString()).join(" * "); typ = prefix + " -> " + typ; } @@ -233,17 +230,42 @@ export class NPropertyInfo extends NMemberInfo { } } +export class NUnionCaseInfo extends NMemberInfo { + public constructor( + DeclaringType: NTypeInfo, + public Tag: number, + Name: string, + Attributes: CustomAttribute[], + public Fields: Array<[string, NTypeInfo]>, + public Invoke: (...args: any[]) => any, + ) { super(DeclaringType, Name, Attributes); } + + public GetFields() { + return this.Fields.map((nt) => new NPropertyInfo(this.DeclaringType, nt[0], nt[1], false, true, [])); + } + + public get_Tag() { + return this.Tag; + } +} + export class NTypeInfo { public static getParameter(i: string) { if (NTypeInfo.parameterCache[i]) { return NTypeInfo.parameterCache[i]; } else { - const p = new NTypeInfo(i, 0, true, [], (_s) => [], () => []); + const p = new NTypeInfo(i, 0, true, [], (_s) => []); this.parameterCache[i] = p; return p; } } + + public static Simple(name: string) { + return new NTypeInfo(name, 0, false, [], ((_s) => [])); + } + private static parameterCache: {[i: string]: NTypeInfo} = {}; + private instantiations: {[i: string]: NTypeInfo} = {}; private mems: NMemberInfo[] = null; @@ -252,20 +274,27 @@ export class NTypeInfo { public genericCount: number, public isGenericParameter: boolean, public generics: NTypeInfo[], - public members: (self: NTypeInfo) => NMemberInfo[], - public cases: () => CaseInfo[]) {} + public members: (self: NTypeInfo) => NMemberInfo[]) {} public GetGenericTypeDefinition() { if (this.genericCount === 0 || this.generics.length === 0) { return this; - } else { - return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members, this.cases); + } else { + return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); + } } - } public MakeGenericType(args: NTypeInfo[]) { if (args.length !== this.genericCount) { throw new Error("invalid generic argument count "); } - return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this.cases); + + const key = args.map((t) => t.toString()).join(", "); + if (this.instantiations[key]) { + return this.instantiations[key]; + } else { + const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members); + this.instantiations[key] = res; + return res; + } } public get_FullName() { @@ -306,7 +335,7 @@ export class NTypeInfo { return this.GetGenericArguments(); } - public GetMembers() { + public GetAllMembers() { if (!this.mems) { if (this.members) { this.mems = this.members(this); @@ -317,31 +346,41 @@ export class NTypeInfo { return this.mems; } + public GetMembers() { + const m = this.GetAllMembers(); + return m.filter((m) => !(m instanceof NUnionCaseInfo)); + } + public GetProperties() { - const m = this.GetMembers(); + const m = this.GetAllMembers(); return m.filter((m) => m instanceof NPropertyInfo) as NPropertyInfo[]; } public GetMethods() { - const m = this.GetMembers(); + const m = this.GetAllMembers(); return m.filter((m) => m instanceof NMethodInfo) as NMethodInfo[]; } public GetProperty(name: string) { - const m = this.GetMembers(); + const m = this.GetAllMembers(); const prop = m.find((m) => m instanceof NPropertyInfo && m.Name === name) as NPropertyInfo; return prop; } public GetMethod(name: string) { - const m = this.GetMembers(); + const m = this.GetAllMembers(); const meth = m.find((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo; return meth; } - public toShortString() { - return this.fullname; + public toString(): string { + if (this.genericCount > 0) { + const args = this.generics.map((t) => t.toString()).join(", "); + return this.fullname + "<" + args + ">"; + } else { + return this.fullname; + } } - public toString() { + public toLongString() { const members = this .GetMembers() @@ -349,6 +388,17 @@ export class NTypeInfo { .map((m) => " " + m.toString()).join("\n"); return "type " + this.fullname + "=\n" + members; } + + public GetHashCode() { + return stringHash(this.toString()); + } + + public Equals(other: NTypeInfo) { + return equals(this, other); + } + public CompareTo(other: NTypeInfo) { + return compare(this, other); + } } export enum MemberKind { @@ -357,26 +407,27 @@ export enum MemberKind { FSharpField = 2, Constructor = 3, Method = 4, + UnionCase = 5, } -export type ParameterInfo = [string, TypeInfo]; +export type ParameterInfo = [string, NTypeInfo]; -export type FieldInfo = [string, TypeInfo, boolean, MemberKind, ParameterInfo[], CustomAttribute[], (...args: any) => any]; +export type FieldInfo = [string, NTypeInfo, boolean, MemberKind, ParameterInfo[], CustomAttribute[], (...args: any) => any]; export interface CustomAttribute { AttributeType: string; AttributeValue: any; } -export type Constructor = new(...args: any[]) => any; +// export type Constructor = new(...args: any[]) => any; -export class CaseInfo { - constructor(public declaringType: TypeInfo, - public tag: number, - public name: string, - public fields?: TypeInfo[]) { - } -} +// export class CaseInfo { +// constructor(public declaringType: TypeInfo, +// public tag: number, +// public name: string, +// public fields?: TypeInfo[]) { +// } +// } const typeCache: { [fullname: string]: NTypeInfo } = {}; @@ -402,6 +453,10 @@ function mkNMemberInfo(self: NTypeInfo, info: FieldInfo): NMemberInfo { ); case MemberKind.Field: return new NFieldInfo(self, info[0], mkNTypeInfo(info[1]), info[2], info[5]); + case MemberKind.UnionCase: + const tag = (info[2] as unknown) as number; + const arr = info[4].map((v) => [v[0], mkNTypeInfo(v[1])]) as Array<[string, NTypeInfo]>; + return new NUnionCaseInfo(self, tag, info[0], info[5], arr, info[6]); default: return null; } @@ -414,26 +469,26 @@ function mkNMemberInfos(fields: () => FieldInfo[]): (self: NTypeInfo) => NMember } } -function mkNTypeInfo(info: TypeInfo): NTypeInfo { - if (typeCache[info.fullname]) { - return typeCache[info.fullname]; - } else { - const gen = info.generics || []; - const cs = info.cases || (() => []); - const fields = info.fields || (() => []); +function mkNTypeInfo(info: NTypeInfo): NTypeInfo { + return info; + // if (typeCache[info.fullname]) { + // return typeCache[info.fullname]; + // } else { + // const gen = info.generics || []; + // const fields = info.fields || (() => []); - const res = new NTypeInfo(info.fullname, gen.length, false, gen.map((a) => mkNTypeInfo(a)), mkNMemberInfos(fields), cs); - typeCache[info.fullname] = res; - return res; - } + // const res = new NTypeInfo(info.fullname, gen.length, false, gen.map((a) => mkNTypeInfo(a)), mkNMemberInfos(fields)); + // typeCache[info.fullname] = res; + // return res; + // } } -export function declareType(fullname: string, generics: NTypeInfo[], members: () => FieldInfo[], cases: () => CaseInfo[]): NTypeInfo { +export function declareType(fullname: string, generics: NTypeInfo[], members: () => FieldInfo[]): NTypeInfo { let gen: NTypeInfo = null; if (typeCache[fullname]) { gen = typeCache[fullname]; } else { - gen = new NTypeInfo(fullname, generics.length, false, [], mkNMemberInfos(members), cases); + gen = new NTypeInfo(fullname, generics.length, false, [], mkNMemberInfos(members)); typeCache[fullname] = gen; } @@ -445,42 +500,41 @@ export function declareType(fullname: string, generics: NTypeInfo[], members: () } export function getGenericParamter(name: string) { - NTypeInfo.getParameter(name); -} - - -export class TypeInfo { - public NewInfo: NTypeInfo; - constructor(public fullname: string, - public generics?: TypeInfo[], - public constructor?: Constructor, - public fields?: () => FieldInfo[], - public cases?: () => CaseInfo[]) { - this.NewInfo = mkNTypeInfo(this); - } - public toString() { - return fullName(this); - } - public Equals(other: TypeInfo) { - return equals(this, other); - } - public CompareTo(other: TypeInfo) { - return compare(this, other); - } -} - -export function getGenerics(t: (TypeInfo|NTypeInfo)): Array<(TypeInfo|NTypeInfo)> { + return NTypeInfo.getParameter(name); +} + +// export class TypeInfo { +// public NewInfo: NTypeInfo; +// constructor(public fullname: string, +// public generics?: TypeInfo[], +// public constructor?: Constructor, +// public fields?: () => FieldInfo[], +// public cases?: () => CaseInfo[]) { +// this.NewInfo = mkNTypeInfo(this); +// } +// public toString() { +// return fullName(this); +// } +// public Equals(other: TypeInfo) { +// return equals(this, other); +// } +// public CompareTo(other: TypeInfo) { +// return compare(this, other); +// } +// } + +export function getGenerics(t: NTypeInfo): NTypeInfo[] { return t.generics != null ? t.generics : []; } -export function equals(t1: (TypeInfo|NTypeInfo), t2: (TypeInfo|NTypeInfo)): boolean { +export function equals(t1: NTypeInfo, t2: NTypeInfo): boolean { return t1.fullname === t2.fullname && equalArraysWith(getGenerics(t1), getGenerics(t2), equals); } // System.Type is not comparable in .NET, but let's implement this // in case users want to create a dictionary with types as keys -export function compare(t1: TypeInfo, t2: TypeInfo): number { +export function compare(t1: NTypeInfo, t2: NTypeInfo): number { if (t1.fullname !== t2.fullname) { return t1.fullname < t2.fullname ? -1 : 1; } else { @@ -488,133 +542,117 @@ export function compare(t1: TypeInfo, t2: TypeInfo): number { } } -export function type(fullname: string, generics?: TypeInfo[], fields?: () => FieldInfo[]): TypeInfo { +export function type(fullname: string, generics?: NTypeInfo[], fields?: () => FieldInfo[]): NTypeInfo { const gen = generics || []; - const f = fields || (() => []); - return new TypeInfo(fullname, gen, null, f); -} - -export function record(fullname: string, generics: TypeInfo[], - constructor: Constructor, fields: () => FieldInfo[]): TypeInfo { - return new TypeInfo(fullname, generics, constructor, fields); -} - -export function customAttributes(info: FieldInfo): CustomAttribute[] { - return info[5]; -} - -export type CaseInfoInput = string | [string, TypeInfo[]]; - -export function union(fullname: string, generics: TypeInfo[], - constructor: Constructor, cases: () => CaseInfoInput[]): TypeInfo { - const t: TypeInfo = new TypeInfo(fullname, generics, constructor, null, () => cases().map((x, i) => - typeof x === "string" ? new CaseInfo(t, i, x) : new CaseInfo(t, i, x[0], x[1]))); - return t; + const f: () => FieldInfo[] = fields || (() => []); + return new NTypeInfo(fullname, gen.length, false, gen, mkNMemberInfos(f)); } -export function tuple(...generics: TypeInfo[]): TypeInfo { - return new TypeInfo("System.Tuple`" + generics.length, generics); +export function tuple(...generics: NTypeInfo[]): NTypeInfo { + return new NTypeInfo("System.Tuple`" + generics.length, generics.length, false, generics, (_s) => []); } -export function delegate(...generics: TypeInfo[]): TypeInfo { - return new TypeInfo("System.Func`" + generics.length, generics); +export function delegate(...generics: NTypeInfo[]): NTypeInfo { + return new NTypeInfo("System.Func`" + generics.length, generics.length, false, generics, (_s) => []); } -export function lambda(argType: TypeInfo, returnType: TypeInfo): TypeInfo { - return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]); +export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { + return new NTypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", 2, false, [argType, returnType], (_s) => []); } -export function option(generic: TypeInfo): TypeInfo { - return new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic]); +export function option(generic: NTypeInfo): NTypeInfo { + return new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [generic], (_s) => []); } -export function list(generic: TypeInfo): TypeInfo { - return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]); +export function list(generic: NTypeInfo): NTypeInfo { + return new NTypeInfo("Microsoft.FSharp.Collections.FSharpList`1", 1, false, [generic], (_s) => []); } -export function array(generic: TypeInfo): TypeInfo { - return new TypeInfo(generic.fullname + "[]", [generic]); +export function array(generic: NTypeInfo): NTypeInfo { + return new NTypeInfo(generic.fullname + "[]", 1, false, [generic], (_s) => []); } -export const obj: TypeInfo = new TypeInfo("System.Object"); -export const unit: TypeInfo = new TypeInfo("Microsoft.FSharp.Core.Unit"); -export const char: TypeInfo = new TypeInfo("System.Char"); -export const string: TypeInfo = new TypeInfo("System.String"); -export const bool: TypeInfo = new TypeInfo("System.Boolean"); -export const int8: TypeInfo = new TypeInfo("System.SByte"); -export const uint8: TypeInfo = new TypeInfo("System.Byte"); -export const int16: TypeInfo = new TypeInfo("System.Int16"); -export const uint16: TypeInfo = new TypeInfo("System.UInt16"); -export const int32: TypeInfo = new TypeInfo("System.Int32"); -export const uint32: TypeInfo = new TypeInfo("System.UInt32"); -export const float32: TypeInfo = new TypeInfo("System.Single"); -export const float64: TypeInfo = new TypeInfo("System.Double"); -export const decimal: TypeInfo = new TypeInfo("System.Decimal"); +export const obj: NTypeInfo = NTypeInfo.Simple("System.Object"); +export const unit: NTypeInfo = NTypeInfo.Simple("Microsoft.FSharp.Core.Unit"); +export const char: NTypeInfo = NTypeInfo.Simple("System.Char"); +export const string: NTypeInfo = NTypeInfo.Simple("System.String"); +export const bool: NTypeInfo = NTypeInfo.Simple("System.Boolean"); +export const int8: NTypeInfo = NTypeInfo.Simple("System.SByte"); +export const uint8: NTypeInfo = NTypeInfo.Simple("System.Byte"); +export const int16: NTypeInfo = NTypeInfo.Simple("System.Int16"); +export const uint16: NTypeInfo = NTypeInfo.Simple("System.UInt16"); +export const int32: NTypeInfo = NTypeInfo.Simple("System.Int32"); +export const uint32: NTypeInfo = NTypeInfo.Simple("System.UInt32"); +export const float32: NTypeInfo = NTypeInfo.Simple("System.Single"); +export const float64: NTypeInfo = NTypeInfo.Simple("System.Double"); +export const decimal: NTypeInfo = NTypeInfo.Simple("System.Decimal"); -export function name(info: FieldInfo | CaseInfo | TypeInfo): string { - if (Array.isArray(info)) { - return info[0]; - } else if (info instanceof CaseInfo) { - return info.name; - } else { - const i = info.fullname.lastIndexOf("."); - return i === -1 ? info.fullname : info.fullname.substr(i + 1); - } -} +// export function name(info: FieldInfo | CaseInfo | TypeInfo): string { +// if (Array.isArray(info)) { +// return info[0]; +// } else if (info instanceof CaseInfo) { +// return info.name; +// } else { +// const i = info.fullname.lastIndexOf("."); +// return i === -1 ? info.fullname : info.fullname.substr(i + 1); +// } +// } -export function fullName(t: TypeInfo): string { - const gen = t.generics != null && !isArray(t) ? t.generics : []; - if (gen.length > 0) { - return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]"; - } else { - return t.fullname; - } -} +// export function fullName(t: TypeInfo): string { +// const gen = t.generics != null && !isArray(t) ? t.generics : []; +// if (gen.length > 0) { +// return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]"; +// } else { +// return t.fullname; +// } +// } -export function namespace(t: TypeInfo) { - const i = t.fullname.lastIndexOf("."); - return i === -1 ? "" : t.fullname.substr(0, i); -} +// export function namespace(t: TypeInfo) { +// const i = t.fullname.lastIndexOf("."); +// return i === -1 ? "" : t.fullname.substr(0, i); +// } -export function isArray(t: TypeInfo): boolean { - return t.fullname.endsWith("[]"); -} +// export function isArray(t: TypeInfo): boolean { +// return t.fullname.endsWith("[]"); +// } -export function getElementType(t: TypeInfo): TypeInfo { - return isArray(t) ? t.generics[0] : null; -} +// export function getElementType(t: TypeInfo): TypeInfo { +// return isArray(t) ? t.generics[0] : null; +// } -export function isGenericType(t: TypeInfo) { - return t.generics != null && t.generics.length > 0; -} +// export function isGenericType(t: TypeInfo) { +// return t.generics != null && t.generics.length > 0; +// } -/** - * This doesn't replace types for fields (records) or cases (unions) - * but it should be enough for type comparison purposes - */ -export function getGenericTypeDefinition(t: TypeInfo) { - return t.generics == null ? t : new TypeInfo(t.fullname, t.generics.map(() => obj)); -} +// /** +// * This doesn't replace types for fields (records) or cases (unions) +// * but it should be enough for type comparison purposes +// */ +// export function getGenericTypeDefinition(t: TypeInfo) { +// return t.generics == null ? t : new TypeInfo(t.fullname, t.generics.map(() => obj)); +// } // FSharpType -export function getUnionCases(t: TypeInfo): CaseInfo[] { - if (t.cases != null) { - return t.cases(); +export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { + const cases = t.GetMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; + if (cases.length > 0) { + return cases; } else { throw new Error(`${t.fullname} is not an F# union type`); } } -export function getRecordElements(t: TypeInfo): FieldInfo[] { - if (t.fields != null) { - return t.fields().filter((arr) => arr[3] === MemberKind.FSharpField); +export function getRecordElements(t: NTypeInfo): NPropertyInfo[] { + const fields = t.GetMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; + if (fields.length > 0) { + return fields; } else { throw new Error(`${t.fullname} is not an F# record type`); } } -export function getTupleElements(t: TypeInfo): TypeInfo[] { +export function getTupleElements(t: NTypeInfo): NTypeInfo[] { if (isTuple(t)) { return t.generics; } else { @@ -622,7 +660,7 @@ export function getTupleElements(t: TypeInfo): TypeInfo[] { } } -export function getFunctionElements(t: TypeInfo): [TypeInfo, TypeInfo] { +export function getFunctionElements(t: NTypeInfo): [NTypeInfo, NTypeInfo] { if (isFunction(t)) { const gen = t.generics; return [gen[0], gen[1]]; @@ -631,56 +669,36 @@ export function getFunctionElements(t: TypeInfo): [TypeInfo, TypeInfo] { } } -export function getTypeProperties(t: TypeInfo): FieldInfo[] { - if (t.fields) { - return t.fields() - .filter((arr) => arr[3] === MemberKind.Property || arr[3] === MemberKind.FSharpField); - } else { - return []; - } -} - -export function isStatic(i: FieldInfo) { - return i[2]; -} - -export function getTypeFields(t: TypeInfo): FieldInfo[] { - if (t.fields) { - return t.fields() - .filter((arr) => arr[3] === MemberKind.Field); +export function isUnion(t: any): boolean { + if (t instanceof NTypeInfo) { + const cases = (t as NTypeInfo).GetMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; + return cases.length > 0; } else { - return []; + return t instanceof Union; } } -export function getTypeMembers(t: TypeInfo): FieldInfo[] { - if (t.fields) { - return t.fields(); +export function isRecord(t: any): boolean { + if (t instanceof NTypeInfo) { + const fields = t.GetMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; + return fields.length > 0; } else { - return []; + return t instanceof Union; } } -export function isUnion(t: any): boolean { - return t instanceof TypeInfo ? t.cases != null : t instanceof Union; -} - -export function isRecord(t: any): boolean { - return t instanceof TypeInfo ? t.fields != null : t instanceof Record; -} - -export function isTuple(t: TypeInfo): boolean { +export function isTuple(t: NTypeInfo): boolean { return t.fullname.startsWith("System.Tuple"); } // In .NET this is false for delegates -export function isFunction(t: TypeInfo): boolean { +export function isFunction(t: NTypeInfo): boolean { return t.fullname === "Microsoft.FSharp.Core.FSharpFunc`2"; } // FSharpValue -export function getUnionFields(v: any, t: TypeInfo): [CaseInfo, any[]] { +export function getUnionFields(v: any, t: NTypeInfo): [NUnionCaseInfo, any[]] { const cases = getUnionCases(t); const case_ = cases[v.tag]; if (case_ == null) { @@ -689,16 +707,16 @@ export function getUnionFields(v: any, t: TypeInfo): [CaseInfo, any[]] { return [case_, v.fields]; } -export function getUnionCaseFields(uci: CaseInfo): FieldInfo[] { - return uci.fields == null ? [] : uci.fields.map(( t, i ) => ["Data" + i, t, false, MemberKind.FSharpField, [], [], null] as FieldInfo); +export function getUnionCaseFields(uci: NUnionCaseInfo): NPropertyInfo[] { + return uci.Fields.map((nt) => new NPropertyInfo(uci.DeclaringType, nt[0], nt[1], false, true, [])); } export function getRecordFields(v: any): any[] { return Object.keys(v).map((k) => v[k]); } -export function getRecordField(v: any, field: FieldInfo): any { - return v[field[0]]; +export function getRecordField(v: any, field: NPropertyInfo): any { + return v[field.Name]; } export function getTupleFields(v: any): any[] { @@ -709,23 +727,25 @@ export function getTupleField(v: any, i: number): any { return v[i]; } -export function makeUnion(uci: CaseInfo, values: any[]): any { - const expectedLength = (uci.fields || []).length; - if (values.length !== expectedLength) { - throw new Error(`Expected an array of length ${expectedLength} but got ${values.length}`); - } - return new uci.declaringType.constructor(uci.tag, uci.name, ...values); +export function makeUnion(uci: NUnionCaseInfo, values: any[]): any { + return uci.Invoke.apply(null, values); + // const expectedLength = (uci.fields || []).length; + // if (values.length !== expectedLength) { + // throw new Error(`Expected an array of length ${expectedLength} but got ${values.length}`); + // } + // return new uci.declaringType.constructor(uci.tag, uci.name, ...values); } -export function makeRecord(t: TypeInfo, values: any[]): any { +export function makeRecord(t: NTypeInfo, values: any[]): any { const fields = getRecordElements(t); if (fields.length !== values.length) { throw new Error(`Expected an array of length ${fields.length} but got ${values.length}`); } - return new t.constructor(...values); + const ctor = t.GetMembers().find((m) => m instanceof NConstructorInfo) as NConstructorInfo; + return ctor.Invoke(null, ...values); } -export function makeTuple(values: any[], t: TypeInfo): any { +export function makeTuple(values: any[], t: NTypeInfo): any { return values; } From 5338cc7aad0a64061230ff3c08f41e8a1c5a06d1 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sun, 7 Apr 2019 10:11:55 +0200 Subject: [PATCH 05/38] most tests working again --- src/Fable.Transforms/AST/AST.Fable.fs | 3 +- src/Fable.Transforms/FSharp2Fable.fs | 67 ++++++++++++---------- src/Fable.Transforms/Fable2Babel.fs | 50 ++++++++++++----- src/Fable.Transforms/Replacements.fs | 52 +++++++++++------ src/fable-library/Reflection.ts | 80 +++++++++++++++++++++------ 5 files changed, 169 insertions(+), 83 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 35573070fd..0abdb60c77 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -67,7 +67,7 @@ type MemberInfoKind = | Field of name : string * typ : Type * isStatic : bool | Method of name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string | Constructor of parameters : ParameterInfo[] * mangledName : string - | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string + | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string * mangledTypeName : string type MemberInfo = { @@ -103,7 +103,6 @@ type ClassImplicitConstructorInfo = type UnionConstructorInfo = { Entity: FSharpEntity - Cases : UnionCaseInfo[] Members : MemberInfo[] EntityName: string IsPublic: bool } diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 9255abf153..e3dec1cb9c 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -788,35 +788,42 @@ let private isIgnoredMember (meth: FSharpMemberOrFunctionOrValue) = | Some ent -> isErasedEntity ent | None -> false) +let private skipAttribute (name : string) = + // TODO: skip all attributes where definiton not known??? + name.StartsWith "Microsoft.FSharp.Core" || name.StartsWith "System.Reflection" + let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = - let types, args = a.ConstructorArguments |> Seq.toArray |> Array.unzip - let ctor = - a.AttributeType.MembersFunctionsAndValues |> Seq.tryPick (fun m -> - if m.IsConstructor then - let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.map (fun p -> p.Type)|> Seq.toArray - if args.Length = pars.Length then - if FSharp.Collections.Array.forall2 (fun ta tp -> tp = ta) types pars then Some m - else None - else - None - else - None - ) - match ctor with - | Some ctor -> - let args = - a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> - match v with - | :? string as str -> Fable.Value(Fable.StringConstant str, None) - | _ -> Fable.Value(Fable.StringConstant (string v), None) + match a.AttributeType.TryFullName with + | Some fullname when not (skipAttribute fullname) -> + + let types, args = a.ConstructorArguments |> Seq.toArray |> Array.unzip + let ctor = + a.AttributeType.MembersFunctionsAndValues |> Seq.tryPick (fun m -> + if m.IsConstructor then + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.map (fun p -> p.Type)|> Seq.toArray + if args.Length = pars.Length then + if FSharp.Collections.Array.forall2 (fun ta tp -> tp = ta) types pars then Some m + else None + else + None + else + None ) - let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType - let x = makeCallFrom com ctx None typ false [] None args ctor - match a.AttributeType.TryFullName with - | Some n -> Some (n,x) - | _ -> None + match ctor with + | Some ctor -> + let args = + a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> + match v with + | :? string as str -> Fable.Value(Fable.StringConstant str, None) + | _ -> Fable.Value(Fable.StringConstant (string v), None) + ) + let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType + let x = makeCallFrom com ctx None typ false [] None args ctor + Some (fullname,x) + | _ -> + None | _ -> - None + None let private transformUnionCases (com:IFableCompiler) ctx (cases : seq) = cases |> Seq.toArray |> Array.map (fun c -> @@ -898,12 +905,13 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar ] elif ent.IsFSharpUnion then + //sprintf "%s: %A" ent.FullName ent.UnionCases.Count |> addWarning com [] None ent.UnionCases |> Seq.mapi (fun i c -> let mangledName = Helpers.unionCaseCompiledName c |> Option.defaultValue c.Name - + let mangledTypeName = getEntityDeclarationName com ent let fields = c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> f.Name, makeType com ctx.GenericArgs f.FieldType) { - Fable.MemberInfo.Kind = Fable.UnionCaseConstructor(i, c.Name, fields, mangledName) + Fable.MemberInfo.Kind = Fable.UnionCaseConstructor(i, c.Name, fields, mangledName, mangledTypeName) Fable.Attributes = c.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } ) @@ -1141,11 +1149,10 @@ let private transformDeclarations (com: FableCompiler) ctx rootEnt rootDecls = com.AddUsedVarName(entityName) // TODO: Check Equality/Comparison attributes let props = transformMemberReflectionInfos com ctx ent - + let info: Fable.UnionConstructorInfo = { Entity = ent Members = props - Cases = transformUnionCases com ctx ent.UnionCases EntityName = entityName IsPublic = isPublicEntity ent } let r = getEntityLocation ent |> makeRange diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 26b745e24e..f87f200e05 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -6,6 +6,8 @@ open Fable.AST open Fable.AST.Babel open System.Collections.Generic open FSharp.Compiler.SourceCodeServices +open System.Linq.Expressions +open Fable.AST.Babel type ReturnStrategy = | Return @@ -368,7 +370,7 @@ module Util = let genMap = let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray Array.zip genParamNames generics |> Map - mems |> Array.choose (fun x -> + mems |> Array.map (fun x -> let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> let value = com.TransformAsExpr(ctx, e) let typ = StringLiteral(fullname) //com.TransformAsExpr(ctx, Fable.Value(Fable.TypeInfo e.Type, None)) @@ -380,15 +382,20 @@ module Util = )) match x.Kind with - | Fable.MemberInfoKind.UnionCaseConstructor(tag, name, pars, mangledName) -> + | Fable.MemberInfoKind.UnionCaseConstructor(tag, name, pars, mangledName, mangledTypeName) -> let parInfos = pars |> Array.map (fun (pn, pt) -> ArrayExpression [| StringLiteral pn; transformTypeInfo com ctx r [||] genMap pt |] :> Expression) let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit let invoke = - let args = pars |> Array.map (fun (pn, pt) -> Identifier(pn)) - let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + let args = pars |> Array.mapi (fun i _ -> Identifier(sprintf "a%d" i)) + + let allArgs = + Array.append + [| NumericLiteral(float tag) :> Expression; StringLiteral(mangledName) :> Expression |] + (Array.map (fun a -> a :> Expression) args) + + let body = NewExpression(Identifier(mangledTypeName), allArgs) :> Expression ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) let res = ArrayExpression [| @@ -397,13 +404,13 @@ module Util = attributes invoke |] :> Expression - Some res + res | Fable.MemberInfoKind.Constructor(pars, mangledName) -> let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit let invoke = - let args = pars |> Array.map (fun p -> Identifier(p.Name)) + let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) @@ -415,7 +422,7 @@ module Util = attributes invoke |] :> Expression - Some res + res | Fable.MemberInfoKind.Method(name, pars, ret, isStatic, mangledName) -> let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) @@ -423,7 +430,7 @@ module Util = let invoke = - let args = pars |> Array.map (fun p -> Identifier(p.Name)) + let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) let args = if isStatic then args else Array.append [| Identifier "__self" |] args @@ -439,7 +446,7 @@ module Util = attributes invoke |] :> Expression - Some res + res | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> let kind = if fsharp then 2.0 else 0.0 let ret = transformTypeInfo com ctx r [||] genMap typ @@ -449,7 +456,7 @@ module Util = ArrayExpression [||]; attributes |] :> Expression - Some res + res | Fable.MemberInfoKind.Field(name, typ, isStatic) -> let ret = transformTypeInfo com ctx r [||] genMap typ let res = @@ -458,7 +465,7 @@ module Util = ArrayExpression [||]; attributes |] :> Expression - Some res + res ) and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo @@ -577,13 +584,25 @@ module Util = transformTypeInfo com ctx r [||] genMap value |] | Replacements.FSharpResult(ok, err) -> - transformRecordReflectionInfo com ctx r ent mems [| + let resultCases = + [| + { Fable.Kind = Fable.MemberInfoKind.UnionCaseConstructor(0, "Ok", [|"value", ok|], "Ok", "_Option.Result"); Fable.Attributes = [||] } + { Fable.Kind = Fable.MemberInfoKind.UnionCaseConstructor(1, "Error", [|"value", err|], "Error", "_Option.Result"); Fable.Attributes = [||] } + |] + transformRecordReflectionInfo com ctx r ent resultCases [| transformTypeInfo com ctx r [||] genMap ok transformTypeInfo com ctx r [||] genMap err |] | Replacements.FSharpChoice gen -> + let garr = List.toArray gen + let cases = + garr |> Array.mapi (fun i t -> + let name = sprintf "Choice%dOf%d" i garr.Length + { Fable.Kind = Fable.MemberInfoKind.UnionCaseConstructor(i, name, [|"value", t|], name, "_Option.Choice"); Fable.Attributes = [||] } + ) + let gen = List.map (transformTypeInfo com ctx r [||] genMap) gen - List.toArray gen |> transformRecordReflectionInfo com ctx r ent mems + List.toArray gen |> transformRecordReflectionInfo com ctx r ent cases | Replacements.FSharpReference gen -> transformRecordReflectionInfo com ctx r ent mems [|transformTypeInfo com ctx r [||] genMap gen|] | _ -> @@ -616,6 +635,7 @@ module Util = let transformValue (com: IBabelCompiler) (ctx: Context) r value: Expression = match value with + //| Fable.TypeDefInf | Fable.TypeInfo t -> transformTypeInfo com ctx r [||] Map.empty t | Fable.Null _ -> upcast NullLiteral(?loc=r) | Fable.UnitConstant -> upcast NullLiteral(?loc=r) // TODO: Use `void 0`? @@ -1592,7 +1612,7 @@ module Util = let body = [Identifier "tag" :> Expression; Identifier "name" :> _; SpreadElement(Identifier "fields") :> _] |> callFunctionWithThisContext None baseRef thisExpr |> ExpressionStatement - declareType com ctx r info.IsPublic [||] info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) + declareType com ctx r info.IsPublic info.Members info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (info: Fable.CompilerGeneratedConstructorInfo) = let args = diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 8f47b82cfa..c2d09076be 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -336,14 +336,22 @@ let makeTypeInfo r t = let makeTypeDefinitionInfo r t = let t = match t with - | Option _ -> Option Any - | Array _ -> Array Any - | List _ -> List Any + | Option _ -> Option (GenericParam "a") + | Array _ -> Array (GenericParam "a") + | List _ -> List (GenericParam "a") | Tuple genArgs -> - genArgs |> List.map (fun _ -> Any) |> Tuple + genArgs |> List.mapi (fun i _ -> (GenericParam (sprintf "a%d" i))) |> Tuple | DeclaredType(ent, genArgs) -> - let genArgs = genArgs |> List.map (fun _ -> Any) + let names = ent.GenericParameters |> Seq.map (fun p -> p.Name) |> Seq.toList + let genArgs = names |> List.map GenericParam DeclaredType(ent, genArgs) + | FunctionType(kind,r) -> + let kind = + match kind with + | LambdaType(a) -> LambdaType (GenericParam "a") + | DelegateType(a) -> a |> List.mapi (fun i _ -> GenericParam (sprintf "a%d" i)) |> DelegateType + FunctionType(kind, GenericParam "b") + | ErasedUnion a -> ErasedUnion a // TODO: Do something with FunctionType and ErasedUnion? | t -> t TypeInfo t |> makeValue r @@ -2607,6 +2615,7 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some + | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some | Some this, "GetTypeInfo" -> Some this | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some @@ -2857,23 +2866,30 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr else fsharpValue methName r t info args | "System.Reflection.MemberInfo" | "System.Reflection.MethodBase" + | "System.Reflection.ConstructorInfo" | "System.Reflection.PropertyInfo" | "System.Reflection.FieldInfo" | "Microsoft.FSharp.Reflection.UnionCaseInfo" + | "FSharp.Reflection.UnionCaseInfo" | "System.Reflection.MethodInfo" -> - match thisArg, info.CompiledName, args with - | Some c, "Invoke", _ -> Helper.InstanceCall(c, "Invoke", t, args) |> Some - | Some c, "get_Name", [] -> Helper.InstanceCall(c, "get_Name", t, []) |> Some - | Some c, "get_DeclaringType", [] -> Helper.InstanceCall(c, "get_DeclaringType", t, []) |> Some - | Some c, "GetCustomAttributes", args -> Helper.InstanceCall(c, "GetCustomAttributes", t, args) |> Some - | Some c, "GetParameters", [] -> Helper.InstanceCall(c, "GetParameters", t, []) |> Some - | Some c, "get_IsStatic", [] -> Helper.InstanceCall(c, "get_IsStatic", t, []) |> Some - | Some c, "get_IsGenericMethod", [] -> Helper.InstanceCall(c, "get_IsGenericMethod", t, []) |> Some - | Some c, "GetGenericMethodDefinition", [] -> Helper.InstanceCall(c, "GetGenericMethodDefinition", t, []) |> Some - | Some c, "get_ReturnType", [] -> Helper.InstanceCall(c, "get_ReturnType", t, []) |> Some - | Some c, "get_ReturnParameter", [] -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some - | Some c, "GetFields", [] -> Helper.InstanceCall(c, "GetFields", t, []) |> Some - | _ -> None + match thisArg, info.CompiledName with + | Some c, "Invoke" -> Helper.InstanceCall(c, "Invoke", t, args) |> Some + | Some c, "get_Name" -> Helper.InstanceCall(c, "get_Name", t, []) |> Some + | Some c, "get_Tag" -> Helper.InstanceCall(c, "get_Tag", t, []) |> Some + | Some c, "get_DeclaringType" -> Helper.InstanceCall(c, "get_DeclaringType", t, []) |> Some + | Some c, "GetCustomAttributes" -> Helper.InstanceCall(c, "GetCustomAttributes", t, args) |> Some + | Some c, "GetParameters" -> Helper.InstanceCall(c, "GetParameters", t, []) |> Some + | Some c, "get_IsStatic" -> Helper.InstanceCall(c, "get_IsStatic", t, []) |> Some + | Some c, "get_IsGenericMethod" -> Helper.InstanceCall(c, "get_IsGenericMethod", t, []) |> Some + | Some c, "GetGenericMethodDefinition" -> Helper.InstanceCall(c, "GetGenericMethodDefinition", t, []) |> Some + | Some c, "get_ReturnType" -> Helper.InstanceCall(c, "get_ReturnType", t, []) |> Some + | Some c, "get_ReturnParameter" -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some + | Some c, "GetFields" -> Helper.InstanceCall(c, "GetFields", t, []) |> Some + | Some c, "get_PropertyType" -> Helper.InstanceCall(c, "get_PropertyType", t, []) |> Some + | _ -> + let isStatic = Option.isNone thisArg + sprintf "UNKNOWN reflection member: { isStatic: %A; name: %A; args: %A }" isStatic info.CompiledName args |> addError com [] None + None // | "Microsoft.FSharp.Reflection.UnionCaseInfo" -> // match thisArg, info.CompiledName with diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 4b57c576fc..a9cceb6885 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -174,14 +174,14 @@ export class NPropertyInfo extends NMemberInfo { public get_GetMethod() { const getterName = "get_" + this.Name; - const mems = this.DeclaringType.GetMembers(); + const mems = this.DeclaringType.GetAllMembers(); const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } } public get_SetMethod() { const getterName = "set_" + this.Name; - const mems = this.DeclaringType.GetMembers(); + const mems = this.DeclaringType.GetAllMembers(); const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } } @@ -265,22 +265,49 @@ export class NTypeInfo { } private static parameterCache: {[i: string]: NTypeInfo} = {}; - private instantiations: {[i: string]: NTypeInfo} = {}; + public generics: NTypeInfo[] = null; + private instantiations: {[i: string]: NTypeInfo} = {}; private mems: NMemberInfo[] = null; + private genericMap: {[name: string]: number} = {}; + private declaration: NTypeInfo = null; constructor( public fullname: string, public genericCount: number, public isGenericParameter: boolean, - public generics: NTypeInfo[], - public members: (self: NTypeInfo) => NMemberInfo[]) {} + _generics: NTypeInfo[], + public members: (self: NTypeInfo) => NMemberInfo[], + decl?: NTypeInfo) { + const g = _generics.filter((t) => !t.isGenericParameter); + if (g.length === genericCount) { + this.generics = g; + } else { + _generics.forEach((g, i) => { + this.genericMap[g.get_Name()] = i; + }); + this.generics = []; + } + this.declaration = decl || null; + } + + public ResolveGeneric(name: string) { + if (this.genericMap[name]) { + return this.generics[this.genericMap[name]]; + } else { + return null; + } + } public GetGenericTypeDefinition() { if (this.genericCount === 0 || this.generics.length === 0) { return this; } else { - return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); + if (this.declaration) { + return this.declaration; + } else { + return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); + } } } @@ -291,7 +318,7 @@ export class NTypeInfo { if (this.instantiations[key]) { return this.instantiations[key]; } else { - const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members); + const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this); this.instantiations[key] = res; return res; } @@ -304,6 +331,10 @@ export class NTypeInfo { const i = this.fullname.lastIndexOf("."); return i === -1 ? "" : this.fullname.substr(0, i); } + public get_Name() { + const i = this.fullname.lastIndexOf("."); + return i === -1 ? this.fullname : this.fullname.substr(i + 1); + } public get_IsArray() { return this.fullname.endsWith("[]"); } @@ -371,6 +402,14 @@ export class NTypeInfo { const meth = m.find((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo; return meth; } + public toFullString(): string { + if (this.genericCount > 0) { + const args = this.generics.map((t) => t.toFullString()).join(", "); + return this.fullname + "<" + args + ">"; + } else { + return this.fullname; + } + } public toString(): string { if (this.genericCount > 0) { @@ -390,7 +429,7 @@ export class NTypeInfo { } public GetHashCode() { - return stringHash(this.toString()); + return stringHash(this.toFullString()); } public Equals(other: NTypeInfo) { @@ -561,7 +600,12 @@ export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { } export function option(generic: NTypeInfo): NTypeInfo { - return new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [generic], (_s) => []); + return new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [generic], (s) => + [ + new NUnionCaseInfo(s, 0, "None", [], [], ((_) => null)), + new NUnionCaseInfo(s, 1, "Some", [], [["Value", generic]], ((args) => args[0])), + ] + ); } export function list(generic: NTypeInfo): NTypeInfo { @@ -635,7 +679,7 @@ export const decimal: NTypeInfo = NTypeInfo.Simple("System.Decimal"); // FSharpType export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { - const cases = t.GetMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; + const cases = t.GetAllMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; if (cases.length > 0) { return cases; } else { @@ -644,7 +688,7 @@ export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { } export function getRecordElements(t: NTypeInfo): NPropertyInfo[] { - const fields = t.GetMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; + const fields = t.GetAllMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; if (fields.length > 0) { return fields; } else { @@ -671,8 +715,8 @@ export function getFunctionElements(t: NTypeInfo): [NTypeInfo, NTypeInfo] { export function isUnion(t: any): boolean { if (t instanceof NTypeInfo) { - const cases = (t as NTypeInfo).GetMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; - return cases.length > 0; + const idx = (t as NTypeInfo).GetAllMembers().findIndex((m) => m instanceof NUnionCaseInfo); + return idx >= 0; } else { return t instanceof Union; } @@ -680,10 +724,10 @@ export function isUnion(t: any): boolean { export function isRecord(t: any): boolean { if (t instanceof NTypeInfo) { - const fields = t.GetMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; - return fields.length > 0; + const idx = t.GetAllMembers().findIndex((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp); + return idx >= 0; } else { - return t instanceof Union; + return t instanceof Record; } } @@ -741,8 +785,8 @@ export function makeRecord(t: NTypeInfo, values: any[]): any { if (fields.length !== values.length) { throw new Error(`Expected an array of length ${fields.length} but got ${values.length}`); } - const ctor = t.GetMembers().find((m) => m instanceof NConstructorInfo) as NConstructorInfo; - return ctor.Invoke(null, ...values); + const ctor = t.GetAllMembers().find((m) => m instanceof NConstructorInfo) as NConstructorInfo; + return ctor.Invoke(...values); } export function makeTuple(values: any[], t: NTypeInfo): any { From f5d6b34cba31703bc96b4f18c02cadceac8c647f Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 8 Apr 2019 09:12:19 +0200 Subject: [PATCH 06/38] passing generic parameter names --- src/Fable.Transforms/Fable2Babel.fs | 10 +-- src/fable-library/Reflection.ts | 109 ++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index f87f200e05..d373ae173c 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -471,13 +471,12 @@ module Util = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression - let genMap = - let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - Array.zip genParamNames generics |> Map + let genParamNames = ent.GenericParameters |> Seq.map (fun x -> StringLiteral x.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression + //let genMap = Array.zip genParamNames generics |> Map let members = transformMemberReflectionInfos com ctx r ent mems generics //let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression let fields = FunctionExpression([||], BlockStatement [| ReturnStatement(ArrayExpression members) :> Statement |]) :> Expression - [|fullnameExpr; upcast ArrayExpression generics; fields|] + [|fullnameExpr; genParamNames; upcast ArrayExpression generics; fields|] |> coreLibCall com ctx None "Reflection" "type" // and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = @@ -521,7 +520,8 @@ module Util = let genericEntity (ent: FSharpEntity) generics = let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression - let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; ArrayExpression generics :> Expression|] + let genericNames = ent.GenericParameters |> Seq.map (fun p -> StringLiteral p.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression + let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; genericNames; ArrayExpression generics :> Expression|] coreLibCall com ctx None "Reflection" "type" args match t with // TODO: Type info forErasedUnion? diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index a9cceb6885..f8579a98ee 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -7,7 +7,7 @@ export class NParameterInfo { constructor( public Name: string, public ParameterType: NTypeInfo, - ) {} + ) { } public toString() { return this.Name + " : " + this.ParameterType.toString(); @@ -37,14 +37,16 @@ export class NMemberInfo { } export class NMethodBase extends NMemberInfo { + public Parameters: NParameterInfo[]; constructor( DeclaringType: NTypeInfo, Name: string, - public Parameters: NParameterInfo[], + parameters: NParameterInfo[], public IsStatic: boolean, attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); + this.Parameters = parameters.map((t) => t.ParameterType.get_ContainsGenericParameters() ? new NParameterInfo(t.Name, DeclaringType.ResolveGeneric(t.ParameterType)) : t); } public GetParameters() { return this.Parameters; } @@ -58,16 +60,18 @@ export class NMethodBase extends NMemberInfo { } export class NMethodInfo extends NMethodBase { + public ReturnType: NTypeInfo; constructor( DeclaringType: NTypeInfo, Name: string, Parameters: NParameterInfo[], - public ReturnType: NTypeInfo, + returnType: NTypeInfo, IsStatic: boolean, private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { super(DeclaringType, Name, Parameters, IsStatic, attributes); + this.ReturnType = returnType.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(returnType) : returnType; } public get_ReturnType() { return this.ReturnType; } @@ -130,14 +134,16 @@ export class NConstructorInfo extends NMethodBase { } export class NFieldInfo extends NMemberInfo { + public Type: NTypeInfo = null; constructor( DeclaringType: NTypeInfo, Name: string, - public Type: NTypeInfo, + type: NTypeInfo, public IsStatic: boolean, attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); + this.Type = type.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(type) : type; } public get_FieldType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } @@ -158,15 +164,17 @@ export class NFieldInfo extends NMemberInfo { } export class NPropertyInfo extends NMemberInfo { + public Type: NTypeInfo = null; constructor( DeclaringType: NTypeInfo, Name: string, - public Type: NTypeInfo, + type: NTypeInfo, public IsStatic: boolean, public IsFSharp: boolean, attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); + this.Type = type.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(type) : type; } public get_PropertyType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } @@ -231,14 +239,19 @@ export class NPropertyInfo extends NMemberInfo { } export class NUnionCaseInfo extends NMemberInfo { + public Fields: Array<[string, NTypeInfo]> = null; + public constructor( DeclaringType: NTypeInfo, public Tag: number, Name: string, Attributes: CustomAttribute[], - public Fields: Array<[string, NTypeInfo]>, + fields: Array<[string, NTypeInfo]>, public Invoke: (...args: any[]) => any, - ) { super(DeclaringType, Name, Attributes); } + ) { + super(DeclaringType, Name, Attributes); + this.Fields = fields.map ((tup) => tup[1].get_ContainsGenericParameters() ? [tup[0], DeclaringType.ResolveGeneric(tup[1])] : tup) as Array<[string, NTypeInfo]>; + } public GetFields() { return this.Fields.map((nt) => new NPropertyInfo(this.DeclaringType, nt[0], nt[1], false, true, [])); @@ -291,11 +304,17 @@ export class NTypeInfo { this.declaration = decl || null; } - public ResolveGeneric(name: string) { - if (this.genericMap[name]) { - return this.generics[this.genericMap[name]]; + public ResolveGeneric(t: NTypeInfo): NTypeInfo { + if (t.isGenericParameter) { + if (this.genericMap[t.fullname]) { + return this.generics[this.genericMap[t.fullname]]; + } else { + return t; + } + } else if (t.genericCount > 0) { + return new NTypeInfo(t.fullname, t.genericCount, false, t.generics.map((ta) => this.ResolveGeneric(ta)), t.members, t.declaration); } else { - return null; + return t; } } @@ -350,6 +369,10 @@ export class NTypeInfo { return this.genericCount > 0 && !this.generics; } + public get_ContainsGenericParameters(): boolean { + return this.isGenericParameter || (this.genericCount > 0 && (this.generics.length === 0 || (this.generics.findIndex((t) => t.get_ContainsGenericParameters()) >= 0))); + } + public GetGenericArguments() { if (this.genericCount > 0) { if (this.generics) { @@ -522,12 +545,24 @@ function mkNTypeInfo(info: NTypeInfo): NTypeInfo { // } } -export function declareType(fullname: string, generics: NTypeInfo[], members: () => FieldInfo[]): NTypeInfo { +export function declareNType(fullname: string, generics: number, members: (self: NTypeInfo, gen: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { + let gen: NTypeInfo = null; + if (typeCache[fullname]) { + gen = typeCache[fullname]; + } else { + const pars = new Array(generics).map((_, i) => getGenericParamter("a" + i)); + gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); + typeCache[fullname] = gen; + } + return gen; +} +export function declareType(fullname: string, genericNames: string[], generics: NTypeInfo[], members: () => FieldInfo[]): NTypeInfo { let gen: NTypeInfo = null; if (typeCache[fullname]) { gen = typeCache[fullname]; } else { - gen = new NTypeInfo(fullname, generics.length, false, [], mkNMemberInfos(members)); + const gargs = genericNames.map((t) => getGenericParamter(t)); + gen = new NTypeInfo(fullname, gargs.length, false, gargs, mkNMemberInfos(members)); typeCache[fullname] = gen; } @@ -581,18 +616,35 @@ export function compare(t1: NTypeInfo, t2: NTypeInfo): number { } } -export function type(fullname: string, generics?: NTypeInfo[], fields?: () => FieldInfo[]): NTypeInfo { +export function type(fullname: string, genericNames?: string[], generics?: NTypeInfo[], fields?: () => FieldInfo[]): NTypeInfo { const gen = generics || []; + const genNames = genericNames || []; const f: () => FieldInfo[] = fields || (() => []); - return new NTypeInfo(fullname, gen.length, false, gen, mkNMemberInfos(f)); + return declareType(fullname, genNames, gen, f); +} + +function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) => TOut[]): TOut[] { + return input.reduce((out, inx, idx) => { + out.push(...selectListFn(inx, idx)); + return out; + }, new Array()); } export function tuple(...generics: NTypeInfo[]): NTypeInfo { - return new NTypeInfo("System.Tuple`" + generics.length, generics.length, false, generics, (_s) => []); + const pars = generics.map((_, i) => "a" + i); + const gen = + declareType("System.Tuple`" + pars.length, pars, [], () => selectMany(pars, (t, i) => + [ + ["Item" + i, getGenericParamter(t), false, MemberKind.Property, [], [], ((_) => null)], + ["get_Item" + i, getGenericParamter(t), false, MemberKind.Method, [], [], ((t) => t[i])], + ], + )); + return gen.MakeGenericType(generics); } export function delegate(...generics: NTypeInfo[]): NTypeInfo { - return new NTypeInfo("System.Func`" + generics.length, generics.length, false, generics, (_s) => []); + const gen = declareNType("System.Func`" + generics.length, generics.length, (self, pars) => []); + return gen.MakeGenericType(generics); } export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { @@ -600,12 +652,23 @@ export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { } export function option(generic: NTypeInfo): NTypeInfo { - return new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [generic], (s) => - [ - new NUnionCaseInfo(s, 0, "None", [], [], ((_) => null)), - new NUnionCaseInfo(s, 1, "Some", [], [["Value", generic]], ((args) => args[0])), - ] - ); + let gen: NTypeInfo = null; + if (typeCache["Microsoft.FSharp.Core.FSharpOption`1"]) { + gen = typeCache["Microsoft.FSharp.Core.FSharpOption`1"]; + } else { + const par = getGenericParamter("a"); + const r = + new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [par], (s) => + [ + new NUnionCaseInfo(s, 0, "None", [], [], ((_) => null)), + new NUnionCaseInfo(s, 1, "Some", [], [["Value", par]], ((args) => args[0])), + ], + ); + gen = r; + typeCache["Microsoft.FSharp.Core.FSharpOption`1"] = r; + } + + return gen.MakeGenericType([generic]); } export function list(generic: NTypeInfo): NTypeInfo { From 0631fcfb48ccc15a8f0d0b31f1d3d50b48c1d974 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 8 Apr 2019 11:05:08 +0200 Subject: [PATCH 07/38] declarations no longer using old reflection-API --- src/Fable.Transforms/Fable2Babel.fs | 122 +++++++++++++++++++-- src/Fable.Transforms/Replacements.fs | 1 + src/fable-library/Reflection.ts | 156 ++++++++++++++++++++------- tests/Main/ExprTests.fs | 25 ++--- 4 files changed, 239 insertions(+), 65 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index d373ae173c..924906a2ba 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -366,10 +366,103 @@ module Util = | Fable.AsPojo(expr, caseRule) -> com.TransformAsExpr(ctx, Replacements.makePojo com r caseRule expr) | Fable.Curry(expr, arity) -> com.TransformAsExpr(ctx, Replacements.curryExprAtRuntime arity expr) - let rec transformMemberReflectionInfos (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = + let rec transformMemberReflectionInfosNew (com : IBabelCompiler) ctx r (self : Expression) (generics : Expression) (ent: FSharpEntity) (mems : Fable.MemberInfo[]) = + let genMap = ent.GenericParameters |> Seq.mapi (fun i x -> x.Name, i) |> Map.ofSeq + + let genMap (name : string) : Option = + match Map.tryFind name genMap with + | Some i -> + MemberExpression(generics, NumericLiteral(float i), true) :> Expression |> Some + | None -> + None + + let newUnionCase (self : Expression) (tag : int) (name : string) (attributes : ArrayExpression) (fields : array) (invoke : ArrowFunctionExpression) = + let info = coreValue com ctx "Reflection" "NUnionCaseInfo" + let fields = fields |> Array.map (fun (n,t) -> ArrayExpression [| StringLiteral n; transformTypeInfo com ctx r [||] genMap t |] :> Expression) + NewExpression(info, [|self; NumericLiteral(float tag); StringLiteral(name); attributes; ArrayExpression fields; invoke|]) :> Expression + + let newParameter (p : Fable.ParameterInfo) = + let par = coreValue com ctx "Reflection" "NParameterInfo" + NewExpression(par, [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |]) :> Expression + + let newConstructor (self : Expression) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = + let ctor = coreValue com ctx "Reflection" "NConstructorInfo" + let parameters = parameters |> Array.map newParameter + NewExpression(ctor, [|self; ArrayExpression parameters; invoke; attributes |]) :> Expression + + let newMethod (self : Expression) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = + let meth = coreValue com ctx "Reflection" "NMethodInfo" + let parameters = parameters |> Array.map newParameter + let ret = transformTypeInfo com ctx r [||] genMap ret + NewExpression(meth, [|self; StringLiteral name; ArrayExpression parameters; ret; BooleanLiteral isStatic; invoke; attributes |]) :> Expression + + let newProperty (self : Expression) (isStatic : bool) (isFSharp : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) = + let prop = coreValue com ctx "Reflection" "NPropertyInfo" + let ret = transformTypeInfo com ctx r [||] genMap ret + NewExpression(prop, [|self; StringLiteral name; ret; BooleanLiteral isStatic; BooleanLiteral isFSharp; attributes|]) :> Expression + + let newField (self : Expression) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) = + let fld = coreValue com ctx "Reflection" "NFieldInfo" + let ret = transformTypeInfo com ctx r [||] genMap ret + NewExpression(fld, [|self; StringLiteral name; ret; BooleanLiteral isStatic; attributes|]) :> Expression + + mems |> Array.map (fun x -> + let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> + let value = com.TransformAsExpr(ctx, e) + let typ = StringLiteral(fullname) //com.TransformAsExpr(ctx, Fable.Value(Fable.TypeInfo e.Type, None)) + + ObjectExpression [| + U3.Case1 (ObjectProperty(StringLiteral "AttributeType", typ)) + U3.Case1 (ObjectProperty(StringLiteral "AttributeValue", value)) + |] :> Expression + )) + + match x.Kind with + | Fable.MemberInfoKind.UnionCaseConstructor(tag, name, pars, mangledName, mangledTypeName) -> + let invoke = + let args = pars |> Array.mapi (fun i _ -> Identifier(sprintf "a%d" i)) + let allArgs = + Array.append + [| NumericLiteral(float tag) :> Expression; StringLiteral(mangledName) :> Expression |] + (Array.map (fun a -> a :> Expression) args) + + let body = NewExpression(Identifier(mangledTypeName), allArgs) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + newUnionCase self tag name attributes pars invoke + + | Fable.MemberInfoKind.Constructor(pars, mangledName) -> + let invoke = + let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) + let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + + newConstructor self attributes pars invoke + + | Fable.MemberInfoKind.Method(name, pars, ret, isStatic, mangledName) -> + let invoke = + let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) + let args = + if isStatic then args + else Array.append [| Identifier "__self" |] args + + let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression + ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) + //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) + + newMethod self isStatic ret name attributes pars invoke + + | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> + newProperty self isStatic fsharp typ name attributes + + | Fable.MemberInfoKind.Field(name, typ, isStatic) -> + newField self isStatic typ name attributes + ) + and transformMemberReflectionInfos (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = let genMap = let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - Array.zip genParamNames generics |> Map + let m = Array.zip genParamNames generics |> Map + fun (name : string) -> Map.tryFind name m + mems |> Array.map (fun x -> let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> let value = com.TransformAsExpr(ctx, e) @@ -473,11 +566,18 @@ module Util = let fullnameExpr = StringLiteral fullname :> Expression let genParamNames = ent.GenericParameters |> Seq.map (fun x -> StringLiteral x.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression //let genMap = Array.zip genParamNames generics |> Map - let members = transformMemberReflectionInfos com ctx r ent mems generics - //let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression - let fields = FunctionExpression([||], BlockStatement [| ReturnStatement(ArrayExpression members) :> Statement |]) :> Expression + + let self = Identifier "self" + let gen = Identifier "gen" + let nMembers = transformMemberReflectionInfosNew com ctx r self gen ent mems + let fields = FunctionExpression([|toPattern self; toPattern gen|], BlockStatement [| ReturnStatement(ArrayExpression nMembers) :> Statement |]) :> Expression [|fullnameExpr; genParamNames; upcast ArrayExpression generics; fields|] - |> coreLibCall com ctx None "Reflection" "type" + |> coreLibCall com ctx None "Reflection" "ntype" + // let members = transformMemberReflectionInfos com ctx r ent mems generics + // //let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression + // let fields = FunctionExpression([||], BlockStatement [| ReturnStatement(ArrayExpression members) :> Statement |]) :> Expression + // [|fullnameExpr; genParamNames; upcast ArrayExpression generics; fields|] + // |> coreLibCall com ctx None "Reflection" "type" // and transformUnionReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = // let fullname = defaultArg ent.TryFullName Naming.unknown @@ -504,14 +604,14 @@ module Util = // [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; cases|] // |> coreLibCall com ctx None "Reflection" "union" - and transformTypeInfo (com: IBabelCompiler) ctx r (mems : Fable.MemberInfo[]) (genMap: Map) t: Expression = + and transformTypeInfo (com: IBabelCompiler) ctx r (mems : Fable.MemberInfo[]) (genMap: string -> Option) t: Expression = let error msg = addErrorAndReturnNull com r msg let primitiveTypeInfo name = coreValue com ctx "Reflection" name let nonGenericTypeInfo fullname = [| StringLiteral fullname :> Expression |] - |> coreLibCall com ctx None "Reflection" "type" + |> coreLibCall com ctx None "Reflection" "ntype" let resolveGenerics generics: Expression[] = generics |> Array.map (transformTypeInfo com ctx r [||] genMap) let genericTypeInfo name genArgs = @@ -522,12 +622,12 @@ module Util = let fullnameExpr = StringLiteral fullname :> Expression let genericNames = ent.GenericParameters |> Seq.map (fun p -> StringLiteral p.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; genericNames; ArrayExpression generics :> Expression|] - coreLibCall com ctx None "Reflection" "type" args + coreLibCall com ctx None "Reflection" "ntype" args match t with // TODO: Type info forErasedUnion? | Fable.ErasedUnion _ | Fable.Any -> primitiveTypeInfo "obj" | Fable.GenericParam name -> - match Map.tryFind name genMap with + match genMap name with | Some t -> t | None -> coreLibCall com ctx None "Reflection" "getGenericParamter" [|StringLiteral name|] | Fable.Unit -> primitiveTypeInfo "unit" @@ -636,7 +736,7 @@ module Util = let transformValue (com: IBabelCompiler) (ctx: Context) r value: Expression = match value with //| Fable.TypeDefInf - | Fable.TypeInfo t -> transformTypeInfo com ctx r [||] Map.empty t + | Fable.TypeInfo t -> transformTypeInfo com ctx r [||] (fun _ -> None) t | Fable.Null _ -> upcast NullLiteral(?loc=r) | Fable.UnitConstant -> upcast NullLiteral(?loc=r) // TODO: Use `void 0`? | Fable.BoolConstant x -> upcast BooleanLiteral(x, ?loc=r) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index c2d09076be..896fc1b0e2 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2622,6 +2622,7 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + | Some this, "MakeGenericType" -> callTypeInfoMethod this "MakeGenericType" t args |> Some | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some | _ -> None diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index f8579a98ee..1777a6f68a 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -9,25 +9,30 @@ export class NParameterInfo { public ParameterType: NTypeInfo, ) { } + public toPrettyString() { + return this.Name + " : " + this.ParameterType.toFullString(); + } + public toString() { - return this.Name + " : " + this.ParameterType.toString(); + return this.ParameterType.toString() + " " + this.Name; } } -export class NMemberInfo { +export abstract class NMemberInfo { constructor( - public DeclaringType: NTypeInfo, - public Name: string, - private attributes: CustomAttribute[], - ) {} + public DeclaringType: NTypeInfo, + public Name: string, + private attributes: CustomAttribute[], + ) {} + public abstract toPrettyString(): string; public get_Name() { return this.Name; } public get_DeclaringType() { return this.DeclaringType; } public GetCustomAttributes(a: (boolean|NTypeInfo), b?: boolean) { if (typeof a === "boolean") { - return this.attributes; + return this.attributes.map((att) => att.AttributeValue); } else if (a.fullname) { return this.attributes.filter((att) => att.AttributeType === a.fullname).map((att) => att.AttributeValue); } else { @@ -36,7 +41,7 @@ export class NMemberInfo { } } -export class NMethodBase extends NMemberInfo { +export abstract class NMethodBase extends NMemberInfo { public Parameters: NParameterInfo[]; constructor( DeclaringType: NTypeInfo, @@ -80,8 +85,8 @@ export class NMethodInfo extends NMethodBase { return new NParameterInfo("Return", this.ReturnType); } - public toString() { - const args = this.Parameters.map((p) => p.toString()).join(", "); + public toPrettyString(): string { + const args = this.Parameters.map((p) => p.toPrettyString()).join(", "); let attPrefix = ""; const atts = this.GetCustomAttributes(true); @@ -91,7 +96,11 @@ export class NMethodInfo extends NMethodBase { let prefix = "member "; if (this.IsStatic) { prefix = "static " + prefix; } - return attPrefix + prefix + this.Name + "(" + args + ") : " + this.ReturnType.toString(); + return attPrefix + prefix + this.Name + "(" + args + ") : " + this.ReturnType.toFullString(); + } + + public toString() { + return this.toPrettyString(); } public Invoke(target: any, ...args: any[]) { @@ -107,17 +116,15 @@ export class NMethodInfo extends NMethodBase { export class NConstructorInfo extends NMethodBase { constructor( DeclaringType: NTypeInfo, - Name: string, Parameters: NParameterInfo[], - IsStatic: boolean, private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { - super(DeclaringType, Name, Parameters, IsStatic, attributes); + super(DeclaringType, ".ctor", Parameters, true, attributes); } - public toString() { - const args = this.Parameters.map((p) => p.toString()).join(", "); + public toPrettyString() { + const args = this.Parameters.map((p) => p.toPrettyString()).join(", "); let attPrefix = ""; const atts = this.GetCustomAttributes(true); @@ -128,6 +135,10 @@ export class NConstructorInfo extends NMethodBase { return attPrefix + "new(" + args + ")"; } + public toString() { + return this.toPrettyString(); + } + public Invoke(...args: any[]) { return this.invoke.apply(null, args); } @@ -148,8 +159,8 @@ export class NFieldInfo extends NMemberInfo { public get_FieldType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } - public toString() { - const typ = this.Type.toString(); + public toPrettyString() { + const typ = this.Type.toFullString(); let prefix = "val "; if (this.IsStatic) { prefix = "static " + prefix; } @@ -161,6 +172,11 @@ export class NFieldInfo extends NMemberInfo { return attPrefix + prefix + this.Name + " : " + typ; } + + public toString() { + return this.toPrettyString(); + } + } export class NPropertyInfo extends NMemberInfo { @@ -204,15 +220,15 @@ export class NPropertyInfo extends NMemberInfo { s.Invoke(target, [...index, value]); } - public toString() { + public toPrettyString() { const g = this.get_GetMethod(); const s = this.get_SetMethod(); - let typ = this.Type.toString(); + let typ = this.Type.toFullString(); if (g && g.Parameters.length > 0) { - const prefix = g.Parameters.map((p) => p.ParameterType.toString()).join(" * "); + const prefix = g.Parameters.map((p) => p.ParameterType.toFullString()).join(" * "); typ = prefix + " -> " + typ; } else if (s && s.Parameters.length > 0) { - const prefix = s.Parameters.slice(0, s.Parameters.length - 1).map((p) => p.ParameterType.toString()).join(" * "); + const prefix = s.Parameters.slice(0, s.Parameters.length - 1).map((p) => p.ParameterType.toFullString()).join(" * "); typ = prefix + " -> " + typ; } @@ -236,6 +252,11 @@ export class NPropertyInfo extends NMemberInfo { return attPrefix + prefix + this.Name + " : " + typ + suffix; } + + public toString() { + return this.toPrettyString(); + } + } export class NUnionCaseInfo extends NMemberInfo { @@ -260,6 +281,17 @@ export class NUnionCaseInfo extends NMemberInfo { public get_Tag() { return this.Tag; } + + public toPrettyString() { + const decl = this.DeclaringType.toFullString(); + const fields = this.Fields.map((tup) => tup[0] + ": " + tup[1].toFullString()).join(" * "); + return decl + "." + this.Name + " of " + fields; + } + + public toString() { + return this.toPrettyString(); + } + } export class NTypeInfo { @@ -295,20 +327,26 @@ export class NTypeInfo { const g = _generics.filter((t) => !t.isGenericParameter); if (g.length === genericCount) { this.generics = g; + if (decl) { + this.genericMap = decl.genericMap; + } } else { _generics.forEach((g, i) => { this.genericMap[g.get_Name()] = i; }); - this.generics = []; + this.generics = _generics; } this.declaration = decl || null; } public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { - if (this.genericMap[t.fullname]) { + if (t.fullname in this.genericMap) { return this.generics[this.genericMap[t.fullname]]; } else { + + const mapping = Object.keys(this.genericMap).map((k) => k + ": " + this.genericMap[k]).join(", "); + throw new Error("cannot resolve " + t.fullname + " { " + mapping + " }"); return t; } } else if (t.genericCount > 0) { @@ -331,10 +369,10 @@ export class NTypeInfo { } public MakeGenericType(args: NTypeInfo[]) { - if (args.length !== this.genericCount) { throw new Error("invalid generic argument count "); } + if (args.length !== this.genericCount) { throw new Error("invalid generic argument count"); } const key = args.map((t) => t.toString()).join(", "); - if (this.instantiations[key]) { + if (key in this.instantiations) { return this.instantiations[key]; } else { const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this); @@ -426,9 +464,16 @@ export class NTypeInfo { return meth; } public toFullString(): string { - if (this.genericCount > 0) { + if (this.isGenericParameter) { + return "'" + this.fullname; + } else if (this.genericCount > 0) { + let name = this.fullname; + const suffix = "`" + this.genericCount; + if (name.endsWith(suffix)) { + name = name.substr(0, name.length - suffix.length); + } const args = this.generics.map((t) => t.toFullString()).join(", "); - return this.fullname + "<" + args + ">"; + return name + "<" + args + ">"; } else { return this.fullname; } @@ -436,19 +481,19 @@ export class NTypeInfo { public toString(): string { if (this.genericCount > 0) { - const args = this.generics.map((t) => t.toString()).join(", "); - return this.fullname + "<" + args + ">"; + const suffix = "`" + this.genericCount; + return this.fullname.endsWith(suffix) ? this.fullname : this.fullname + suffix; } else { return this.fullname; } } - public toLongString() { + public toPrettyString() { const members = this .GetMembers() .filter((m) => !(m instanceof NMethodInfo) || !(m.Name.startsWith("get_") || m.Name.startsWith("set_"))) - .map((m) => " " + m.toString()).join("\n"); - return "type " + this.fullname + "=\n" + members; + .map((m) => " " + m.toPrettyString()).join("\n"); + return "type " + this.toFullString() + "=\n" + members; } public GetHashCode() { @@ -510,8 +555,8 @@ function mkNMemberInfo(self: NTypeInfo, info: FieldInfo): NMemberInfo { return new NPropertyInfo(self, info[0], mkNTypeInfo(info[1]), info[2], true, info[5]); case MemberKind.Constructor: return new NConstructorInfo( - self, info[0], info[4].map((a) => mkParameterInfo(a)), - info[2], info[6], info[5], + self, info[4].map((a) => mkParameterInfo(a)), + info[6], info[5], ); case MemberKind.Field: return new NFieldInfo(self, info[0], mkNTypeInfo(info[1]), info[2], info[5]); @@ -547,18 +592,19 @@ function mkNTypeInfo(info: NTypeInfo): NTypeInfo { export function declareNType(fullname: string, generics: number, members: (self: NTypeInfo, gen: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { let gen: NTypeInfo = null; - if (typeCache[fullname]) { + if (fullname in typeCache) { gen = typeCache[fullname]; } else { const pars = new Array(generics).map((_, i) => getGenericParamter("a" + i)); - gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); + const mems = members || ((_self, _gen) => []); + gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => mems(s, pars)); typeCache[fullname] = gen; } return gen; } export function declareType(fullname: string, genericNames: string[], generics: NTypeInfo[], members: () => FieldInfo[]): NTypeInfo { let gen: NTypeInfo = null; - if (typeCache[fullname]) { + if (fullname in typeCache) { gen = typeCache[fullname]; } else { const gargs = genericNames.map((t) => getGenericParamter(t)); @@ -623,6 +669,31 @@ export function type(fullname: string, genericNames?: string[], generics?: NType return declareType(fullname, genNames, gen, f); } +export function ntype(fullname: string, genericNames?: string[], generics?: NTypeInfo[], members?: (self: NTypeInfo, pars: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { + let gen: NTypeInfo = null; + if (fullname in typeCache) { + gen = typeCache[fullname]; + } else { + genericNames = genericNames || []; + generics = generics || []; + members = members || ((_s, _g) => []); + + const pars = genericNames.map((n) => getGenericParamter(n)); + gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); + typeCache[fullname] = gen; + } + + if (generics.length > 0) { + return gen.MakeGenericType(generics); + } else { + return gen; + } + // const gen = generics || []; + + // const genNames = genericNames || []; + // const f = fields || ((_self, _gen) => []); + // return declareType(fullname, genNames, gen, f); +} function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) => TOut[]): TOut[] { return input.reduce((out, inx, idx) => { out.push(...selectListFn(inx, idx)); @@ -653,7 +724,7 @@ export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { export function option(generic: NTypeInfo): NTypeInfo { let gen: NTypeInfo = null; - if (typeCache["Microsoft.FSharp.Core.FSharpOption`1"]) { + if ("Microsoft.FSharp.Core.FSharpOption`1" in typeCache) { gen = typeCache["Microsoft.FSharp.Core.FSharpOption`1"]; } else { const par = getGenericParamter("a"); @@ -672,7 +743,12 @@ export function option(generic: NTypeInfo): NTypeInfo { } export function list(generic: NTypeInfo): NTypeInfo { - return new NTypeInfo("Microsoft.FSharp.Collections.FSharpList`1", 1, false, [generic], (_s) => []); + const gen = declareNType("Microsoft.FSharp.Collections.FSharpList`1", 1, (self, gen) => [ + new NPropertyInfo(self, "Head", gen[0], false, false, []), + new NMethodInfo(self, "get_Head", [], gen[0], false, ((l) => l[0]), []), + ]); + return gen.MakeGenericType([generic]); + // return new NTypeInfo("Microsoft.FSharp.Collections.FSharpList`1", 1, false, [generic], (_s) => []); } export function array(generic: NTypeInfo): NTypeInfo { diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 6518d14cc2..876733ccde 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -6,6 +6,13 @@ open Fable.Tests open System.Globalization open FSharp.Quotations.Patterns + +type Heinz<'a>(value : 'a) = + member x.Sepp = [value] + + member x.Self(v : 'a) = Heinz(v) + + type Bla() = member x.Delay (f : unit -> 'a) = f() @@ -47,19 +54,9 @@ open FSharp.Quotations.Patterns open Fable.Core type System.Type with - [] - member x.NewInfo : obj = jsNative - -type System.Reflection.MemberInfo with - - [] - member x.Invoke1 (v : 'a) : 'b = Util.jsNative - - [] - member x.Invoke0 () : 'b = Util.jsNative - [] - member x.IsStatic : bool = Util.jsNative + [] + member x.ToPrettyString() : string = jsNative type Sepp(a : int, b : string) = member x.Yeah = b @@ -172,6 +169,6 @@ let tests = testCase "CustomAttributes" <| fun () -> - let prop = typeof.GetMembers() //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) - failwithf "prop: %A" prop + let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) + failwithf "prop: %s" (prop.ToPrettyString()) ] \ No newline at end of file From 8d54ed1bbcf997552755dee24c636cd40108710d Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 8 Apr 2019 14:44:32 +0200 Subject: [PATCH 08/38] removed old Reflection API (fixed generic types) --- src/Fable.Transforms/Fable2Babel.fs | 3 +- src/fable-library/Reflection.ts | 252 ++++++++++------------------ 2 files changed, 86 insertions(+), 169 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 924906a2ba..b3cd6c8fb2 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -615,7 +615,8 @@ module Util = let resolveGenerics generics: Expression[] = generics |> Array.map (transformTypeInfo com ctx r [||] genMap) let genericTypeInfo name genArgs = - let resolved = resolveGenerics genArgs + let gen = genArgs |> Array.exists (function Fable.GenericParam _ -> true | _ -> false) + let resolved = if gen then [||] else resolveGenerics genArgs coreLibCall com ctx None "Reflection" name resolved let genericEntity (ent: FSharpEntity) generics = let fullname = defaultArg ent.TryFullName Naming.unknown diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 1777a6f68a..7c3086b040 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1,4 +1,4 @@ -import { Record, Union } from "./Types"; +import { Record, Union, declare } from "./Types"; import { compareArraysWith, equalArraysWith, stringHash } from "./Util"; // tslint:disable: max-line-length @@ -336,13 +336,21 @@ export class NTypeInfo { }); this.generics = _generics; } + if (this.generics.length !== this.genericCount) { throw new Error("invalid generic count"); } this.declaration = decl || null; + + if (fullname === "Microsoft.FSharp.Collections.FSharpList`1" && this.generics.length === 0) { + throw new Error("bad list type"); + } + } public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genericMap) { - return this.generics[this.genericMap[t.fullname]]; + const idx = this.genericMap[t.fullname]; + if (idx < 0 || idx >= this.generics.length) { throw new Error("cannot resolve: " + t.toFullString()); } + return this.generics[idx]; } else { const mapping = Object.keys(this.genericMap).map((k) => k + ": " + this.genericMap[k]).join(", "); @@ -357,18 +365,19 @@ export class NTypeInfo { } public GetGenericTypeDefinition() { - if (this.genericCount === 0 || this.generics.length === 0) { + if (this.genericCount === 0 || this.get_IsGenericTypeDefinition()) { return this; + } else if (this.declaration) { + return this.declaration; } else { - if (this.declaration) { - return this.declaration; - } else { - return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); - } + throw new Error("bad HATE"); + return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); } } public MakeGenericType(args: NTypeInfo[]) { + args = args.filter((a) => a); + if (args.length === 0) { return this; } if (args.length !== this.genericCount) { throw new Error("invalid generic argument count"); } const key = args.map((t) => t.toString()).join(", "); @@ -404,7 +413,12 @@ export class NTypeInfo { return this.genericCount > 0; } public get_IsGenericTypeDefinition() { - return this.genericCount > 0 && !this.generics; + if (this.genericCount > 0) { + const idx = this.generics.findIndex((g) => g.isGenericParameter); + return idx >= 0; + } else { + return false; + } } public get_ContainsGenericParameters(): boolean { @@ -413,7 +427,7 @@ export class NTypeInfo { public GetGenericArguments() { if (this.genericCount > 0) { - if (this.generics) { + if (this.generics.length > 0) { return this.generics; } else { return Array(this.genericCount).map((_, i) => NTypeInfo.getParameter(i.toString())); @@ -508,148 +522,50 @@ export class NTypeInfo { } } -export enum MemberKind { - Property = 0, - Field = 1, - FSharpField = 2, - Constructor = 3, - Method = 4, - UnionCase = 5, -} - -export type ParameterInfo = [string, NTypeInfo]; - -export type FieldInfo = [string, NTypeInfo, boolean, MemberKind, ParameterInfo[], CustomAttribute[], (...args: any) => any]; - export interface CustomAttribute { AttributeType: string; AttributeValue: any; } -// export type Constructor = new(...args: any[]) => any; - -// export class CaseInfo { -// constructor(public declaringType: TypeInfo, -// public tag: number, -// public name: string, -// public fields?: TypeInfo[]) { -// } -// } - const typeCache: { [fullname: string]: NTypeInfo } = {}; -function mkParameterInfo(info: ParameterInfo) { - return new NParameterInfo(info[0], mkNTypeInfo(info[1])); -} - -function mkNMemberInfo(self: NTypeInfo, info: FieldInfo): NMemberInfo { - switch (info[3]) { - case MemberKind.Method: - return new NMethodInfo( - self, info[0], info[4].map((a) => mkParameterInfo(a)), - mkNTypeInfo(info[1]), info[2], info[6], info[5], - ); - case MemberKind.Property: - return new NPropertyInfo(self, info[0], mkNTypeInfo(info[1]), info[2], false, info[5]); - case MemberKind.FSharpField: - return new NPropertyInfo(self, info[0], mkNTypeInfo(info[1]), info[2], true, info[5]); - case MemberKind.Constructor: - return new NConstructorInfo( - self, info[4].map((a) => mkParameterInfo(a)), - info[6], info[5], - ); - case MemberKind.Field: - return new NFieldInfo(self, info[0], mkNTypeInfo(info[1]), info[2], info[5]); - case MemberKind.UnionCase: - const tag = (info[2] as unknown) as number; - const arr = info[4].map((v) => [v[0], mkNTypeInfo(v[1])]) as Array<[string, NTypeInfo]>; - return new NUnionCaseInfo(self, tag, info[0], info[5], arr, info[6]); - default: - return null; - } -} -function mkNMemberInfos(fields: () => FieldInfo[]): (self: NTypeInfo) => NMemberInfo[] { - if (fields) { - return (self: NTypeInfo) => fields().map((f) => mkNMemberInfo(self, f)).filter((f) => f !== null); - } else { - return (_self: NTypeInfo) => []; - } -} - -function mkNTypeInfo(info: NTypeInfo): NTypeInfo { - return info; - // if (typeCache[info.fullname]) { - // return typeCache[info.fullname]; - // } else { - // const gen = info.generics || []; - // const fields = info.fields || (() => []); - - // const res = new NTypeInfo(info.fullname, gen.length, false, gen.map((a) => mkNTypeInfo(a)), mkNMemberInfos(fields)); - // typeCache[info.fullname] = res; - // return res; - // } -} - export function declareNType(fullname: string, generics: number, members: (self: NTypeInfo, gen: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { let gen: NTypeInfo = null; if (fullname in typeCache) { gen = typeCache[fullname]; } else { - const pars = new Array(generics).map((_, i) => getGenericParamter("a" + i)); + const pars = Array.from({ length: generics }, (_, i) => getGenericParamter("a" + i)); + const mems = members || ((_self, _gen) => []); gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => mems(s, pars)); typeCache[fullname] = gen; } return gen; } -export function declareType(fullname: string, genericNames: string[], generics: NTypeInfo[], members: () => FieldInfo[]): NTypeInfo { - let gen: NTypeInfo = null; - if (fullname in typeCache) { - gen = typeCache[fullname]; - } else { - const gargs = genericNames.map((t) => getGenericParamter(t)); - gen = new NTypeInfo(fullname, gargs.length, false, gargs, mkNMemberInfos(members)); - typeCache[fullname] = gen; - } - - if (generics.length > 0 && generics.length === gen.genericCount) { - return gen.MakeGenericType(generics); - } else { - return gen; - } -} export function getGenericParamter(name: string) { return NTypeInfo.getParameter(name); } -// export class TypeInfo { -// public NewInfo: NTypeInfo; -// constructor(public fullname: string, -// public generics?: TypeInfo[], -// public constructor?: Constructor, -// public fields?: () => FieldInfo[], -// public cases?: () => CaseInfo[]) { -// this.NewInfo = mkNTypeInfo(this); -// } -// public toString() { -// return fullName(this); -// } -// public Equals(other: TypeInfo) { -// return equals(this, other); -// } -// public CompareTo(other: TypeInfo) { -// return compare(this, other); -// } -// } - export function getGenerics(t: NTypeInfo): NTypeInfo[] { - return t.generics != null ? t.generics : []; + const gen = t.generics || []; + // const badIdx = gen.findIndex((t) => !t); + // if (badIdx >= 0) { throw new Error("bad generic arg: " + badIdx); } + return gen; } export function equals(t1: NTypeInfo, t2: NTypeInfo): boolean { - return t1.fullname === t2.fullname - && equalArraysWith(getGenerics(t1), getGenerics(t2), equals); + if (t1.fullname === t2.fullname) { + const g1 = getGenerics(t1); + const g2 = getGenerics(t2); + try { + return equalArraysWith(g1, g2, equals); + } catch (e) { + throw new Error(t1.fullname + " g1: " + g1 + " g2: " + g2); + } + } else { + return false; + } } // System.Type is not comparable in .NET, but let's implement this @@ -662,21 +578,19 @@ export function compare(t1: NTypeInfo, t2: NTypeInfo): number { } } -export function type(fullname: string, genericNames?: string[], generics?: NTypeInfo[], fields?: () => FieldInfo[]): NTypeInfo { - const gen = generics || []; - const genNames = genericNames || []; - const f: () => FieldInfo[] = fields || (() => []); - return declareType(fullname, genNames, gen, f); -} - export function ntype(fullname: string, genericNames?: string[], generics?: NTypeInfo[], members?: (self: NTypeInfo, pars: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { let gen: NTypeInfo = null; + generics = generics || []; + const a = generics.findIndex((t) => !t); + if (a >= 0) { throw new Error("bad hate occured"); } + if (fullname in typeCache) { gen = typeCache[fullname]; } else { - genericNames = genericNames || []; - generics = generics || []; members = members || ((_s, _g) => []); + genericNames = genericNames || []; + const b = genericNames.findIndex((t) => !t); + if (b >= 0) { throw new Error("bad hate occured"); } const pars = genericNames.map((n) => getGenericParamter(n)); gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); @@ -688,11 +602,6 @@ export function ntype(fullname: string, genericNames?: string[], generics?: NTyp } else { return gen; } - // const gen = generics || []; - - // const genNames = genericNames || []; - // const f = fields || ((_self, _gen) => []); - // return declareType(fullname, genNames, gen, f); } function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) => TOut[]): TOut[] { return input.reduce((out, inx, idx) => { @@ -702,47 +611,48 @@ function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) = } export function tuple(...generics: NTypeInfo[]): NTypeInfo { - const pars = generics.map((_, i) => "a" + i); + const name = "System.Tuple`" + generics.length; const gen = - declareType("System.Tuple`" + pars.length, pars, [], () => selectMany(pars, (t, i) => - [ - ["Item" + i, getGenericParamter(t), false, MemberKind.Property, [], [], ((_) => null)], - ["get_Item" + i, getGenericParamter(t), false, MemberKind.Method, [], [], ((t) => t[i])], - ], - )); + declareNType(name, generics.length, (self, gen) => selectMany(gen, (t, i) => [ + new NPropertyInfo(self, "Item" + i, t, false, false, []), + new NMethodInfo(self, "get_Item" + i, [], t, false, ((a) => a[i]), []), + ])); return gen.MakeGenericType(generics); } export function delegate(...generics: NTypeInfo[]): NTypeInfo { - const gen = declareNType("System.Func`" + generics.length, generics.length, (self, pars) => []); + const name = "System.Func`" + generics.length; + const gen = + declareNType(name, generics.length, (self, gen) => { + const ret = generics[generics.length - 1]; + const args = generics.slice(0, generics.length - 1).map((t, i) => new NParameterInfo("arg" + i, t)); + return [ + new NMethodInfo(self, "Invoke", args, ret, false, ((target, ...args) => target(...args)), []), + ]; + }); return gen.MakeGenericType(generics); } export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { - return new NTypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", 2, false, [argType, returnType], (_s) => []); + const name = "Microsoft.FSharp.Core.FSharpFunc`2"; + const gen = + declareNType(name, 2, (self, gen) => [ + new NMethodInfo(self, "Invoke", [new NParameterInfo("arg", gen[0])], gen[1], false, ((target, ...args) => target(...args)), []), + ]); + return gen.MakeGenericType([argType, returnType]); } -export function option(generic: NTypeInfo): NTypeInfo { - let gen: NTypeInfo = null; - if ("Microsoft.FSharp.Core.FSharpOption`1" in typeCache) { - gen = typeCache["Microsoft.FSharp.Core.FSharpOption`1"]; - } else { - const par = getGenericParamter("a"); - const r = - new NTypeInfo("Microsoft.FSharp.Core.FSharpOption`1", 1, false, [par], (s) => - [ - new NUnionCaseInfo(s, 0, "None", [], [], ((_) => null)), - new NUnionCaseInfo(s, 1, "Some", [], [["Value", par]], ((args) => args[0])), - ], - ); - gen = r; - typeCache["Microsoft.FSharp.Core.FSharpOption`1"] = r; - } - +export function option(generic?: NTypeInfo): NTypeInfo { + const name = "Microsoft.FSharp.Core.FSharpOption`1"; + const gen = + declareNType(name, 2, (self, gen) => [ + new NUnionCaseInfo(self, 0, "None", [], [], ((_) => null)), + new NUnionCaseInfo(self, 1, "Some", [], [["Value", gen[0]]], ((args, ...rest) => args)), + ]); return gen.MakeGenericType([generic]); } -export function list(generic: NTypeInfo): NTypeInfo { +export function list(generic?: NTypeInfo): NTypeInfo { const gen = declareNType("Microsoft.FSharp.Collections.FSharpList`1", 1, (self, gen) => [ new NPropertyInfo(self, "Head", gen[0], false, false, []), new NMethodInfo(self, "get_Head", [], gen[0], false, ((l) => l[0]), []), @@ -751,8 +661,14 @@ export function list(generic: NTypeInfo): NTypeInfo { // return new NTypeInfo("Microsoft.FSharp.Collections.FSharpList`1", 1, false, [generic], (_s) => []); } -export function array(generic: NTypeInfo): NTypeInfo { - return new NTypeInfo(generic.fullname + "[]", 1, false, [generic], (_s) => []); +export function array(generic?: NTypeInfo): NTypeInfo { + const gen = declareNType("_[]", 1, (self, gen) => [ + new NPropertyInfo(self, "Length", int32, false, false, []), + new NMethodInfo(self, "get_Length", [], int32, false, ((l) => l.length), []), + ]); + return gen.MakeGenericType([generic]); + // generic = generic || getGenericParamter("a"); + // return new NTypeInfo(generic.fullname + "[]", 1, false, [generic], (_s) => []); } export const obj: NTypeInfo = NTypeInfo.Simple("System.Object"); From 6ef3907fdc3594ce1909875a635ee23bf0c170c1 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 8 Apr 2019 18:48:54 +0200 Subject: [PATCH 09/38] added support for GenericMethods --- src/Fable.Transforms/AST/AST.Fable.fs | 2 +- src/Fable.Transforms/FSharp2Fable.fs | 9 +- src/Fable.Transforms/Fable2Babel.fs | 116 ++------------------------ src/Fable.Transforms/Replacements.fs | 41 ++++----- src/fable-library/Reflection.ts | 78 ++++++++++++----- tests/Main/ReflectionTests.fs | 10 +++ 6 files changed, 103 insertions(+), 153 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 0abdb60c77..7ba1507fc0 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -65,7 +65,7 @@ type ParameterInfo = type MemberInfoKind = | Property of name : string * typ : Type * fsharp : bool * isStatic : bool | Field of name : string * typ : Type * isStatic : bool - | Method of name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string + | Method of genericParameters : string[] * name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string | Constructor of parameters : ParameterInfo[] * mangledName : string | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string * mangledTypeName : string diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index e3dec1cb9c..8336ceda1f 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -874,15 +874,12 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } ) let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type - let mangledName = Helpers.getMemberDeclarationName com m - + let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) Some { - Fable.MemberInfo.Kind = Fable.Method(m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) + Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - // else - // None + } ) let special = diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index b3cd6c8fb2..00312cfbea 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -390,11 +390,14 @@ module Util = let parameters = parameters |> Array.map newParameter NewExpression(ctor, [|self; ArrayExpression parameters; invoke; attributes |]) :> Expression - let newMethod (self : Expression) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = + let newMethod (self : Expression) (genericParameters : string[]) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = let meth = coreValue com ctx "Reflection" "NMethodInfo" let parameters = parameters |> Array.map newParameter let ret = transformTypeInfo com ctx r [||] genMap ret - NewExpression(meth, [|self; StringLiteral name; ArrayExpression parameters; ret; BooleanLiteral isStatic; invoke; attributes |]) :> Expression + + let genPars = genericParameters |> Array.map (fun n -> coreLibCall com ctx None "Reflection" "getGenericParameter" [| StringLiteral n |]) |> ArrayExpression :> Expression + + NewExpression(meth, [|self; genPars; StringLiteral name; ArrayExpression parameters; ret; BooleanLiteral isStatic; invoke; attributes |]) :> Expression let newProperty (self : Expression) (isStatic : bool) (isFSharp : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) = let prop = coreValue com ctx "Reflection" "NPropertyInfo" @@ -438,7 +441,7 @@ module Util = newConstructor self attributes pars invoke - | Fable.MemberInfoKind.Method(name, pars, ret, isStatic, mangledName) -> + | Fable.MemberInfoKind.Method(genericParameters, name, pars, ret, isStatic, mangledName) -> let invoke = let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) let args = @@ -449,7 +452,7 @@ module Util = ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) - newMethod self isStatic ret name attributes pars invoke + newMethod self genericParameters isStatic ret name attributes pars invoke | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> newProperty self isStatic fsharp typ name attributes @@ -457,109 +460,6 @@ module Util = | Fable.MemberInfoKind.Field(name, typ, isStatic) -> newField self isStatic typ name attributes ) - and transformMemberReflectionInfos (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = - let genMap = - let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - let m = Array.zip genParamNames generics |> Map - fun (name : string) -> Map.tryFind name m - - mems |> Array.map (fun x -> - let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> - let value = com.TransformAsExpr(ctx, e) - let typ = StringLiteral(fullname) //com.TransformAsExpr(ctx, Fable.Value(Fable.TypeInfo e.Type, None)) - - ObjectExpression [| - U3.Case1 (ObjectProperty(StringLiteral "AttributeType", typ)) - U3.Case1 (ObjectProperty(StringLiteral "AttributeValue", value)) - |] :> Expression - )) - - match x.Kind with - | Fable.MemberInfoKind.UnionCaseConstructor(tag, name, pars, mangledName, mangledTypeName) -> - let parInfos = pars |> Array.map (fun (pn, pt) -> ArrayExpression [| StringLiteral pn; transformTypeInfo com ctx r [||] genMap pt |] :> Expression) - let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit - - let invoke = - let args = pars |> Array.mapi (fun i _ -> Identifier(sprintf "a%d" i)) - - let allArgs = - Array.append - [| NumericLiteral(float tag) :> Expression; StringLiteral(mangledName) :> Expression |] - (Array.map (fun a -> a :> Expression) args) - - let body = NewExpression(Identifier(mangledTypeName), allArgs) :> Expression - ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - - let res = - ArrayExpression [| - StringLiteral name; ret; NumericLiteral (float tag); NumericLiteral(5.0); - ArrayExpression parInfos; - attributes - invoke - |] :> Expression - res - | Fable.MemberInfoKind.Constructor(pars, mangledName) -> - let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) - let ret = transformTypeInfo com ctx r [||] genMap Fable.Type.Unit - - let invoke = - let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) - let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression - ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) - - let res = - ArrayExpression [| - StringLiteral ".ctor"; ret; BooleanLiteral true; NumericLiteral(3.0); - ArrayExpression parInfos; - attributes - invoke - |] :> Expression - res - - | Fable.MemberInfoKind.Method(name, pars, ret, isStatic, mangledName) -> - let parInfos = pars |> Array.map (fun p -> ArrayExpression [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |] :> Expression) - let ret = transformTypeInfo com ctx r [||] genMap ret - - - let invoke = - let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) - let args = - if isStatic then args - else Array.append [| Identifier "__self" |] args - - let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression - ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) - - let res = - ArrayExpression [| - StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(4.0); - ArrayExpression parInfos; - attributes - invoke - |] :> Expression - res - | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> - let kind = if fsharp then 2.0 else 0.0 - let ret = transformTypeInfo com ctx r [||] genMap typ - let res = - ArrayExpression [| - StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(kind); - ArrayExpression [||]; - attributes - |] :> Expression - res - | Fable.MemberInfoKind.Field(name, typ, isStatic) -> - let ret = transformTypeInfo com ctx r [||] genMap typ - let res = - ArrayExpression [| - StringLiteral name; ret; BooleanLiteral isStatic; NumericLiteral(1.0); - ArrayExpression [||]; - attributes - |] :> Expression - res - ) and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo let fullname = defaultArg ent.TryFullName Naming.unknown @@ -630,7 +530,7 @@ module Util = | Fable.GenericParam name -> match genMap name with | Some t -> t - | None -> coreLibCall com ctx None "Reflection" "getGenericParamter" [|StringLiteral name|] + | None -> coreLibCall com ctx None "Reflection" "getGenericParameter" [|StringLiteral name|] | Fable.Unit -> primitiveTypeInfo "unit" | Fable.Boolean -> primitiveTypeInfo "bool" | Fable.Char -> primitiveTypeInfo "char" diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 896fc1b0e2..ef3eb64f44 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2607,23 +2607,23 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio match thisArg, i.CompiledName with - | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some - | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some - | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some - | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some - | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some - | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some - | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some - | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some - | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some - | Some this, "GetTypeInfo" -> Some this - | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some - | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some - | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some - | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some - - | Some this, "MakeGenericType" -> callTypeInfoMethod this "MakeGenericType" t args |> Some - | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some + | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some + | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some + | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some + | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some + | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some + | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some + | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some + | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some + | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some + | Some this, "GetTypeInfo" -> Some this + | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some + | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some + | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some + | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + + | Some this, "MakeGenericType" -> callTypeInfoMethod this "MakeGenericType" t args |> Some + | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some | _ -> None // match thisArg with @@ -2874,6 +2874,11 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | "FSharp.Reflection.UnionCaseInfo" | "System.Reflection.MethodInfo" -> match thisArg, info.CompiledName with + + | Some this, "GetGenericMethodDefinition" -> callTypeInfoMethod this "GetGenericMethodDefinition" t [] |> Some + | Some this, "get_IsGenericMethod" -> callTypeInfoMethod this "get_IsGenericMethod" t [] |> Some + | Some this, "get_IsGenericMethodDefinition" -> callTypeInfoMethod this "get_IsGenericMethodDefinition" t [] |> Some + | Some this, "MakeGenericMethod" -> callTypeInfoMethod this "MakeGenericMethod" t args |> Some | Some c, "Invoke" -> Helper.InstanceCall(c, "Invoke", t, args) |> Some | Some c, "get_Name" -> Helper.InstanceCall(c, "get_Name", t, []) |> Some | Some c, "get_Tag" -> Helper.InstanceCall(c, "get_Tag", t, []) |> Some @@ -2881,8 +2886,6 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | Some c, "GetCustomAttributes" -> Helper.InstanceCall(c, "GetCustomAttributes", t, args) |> Some | Some c, "GetParameters" -> Helper.InstanceCall(c, "GetParameters", t, []) |> Some | Some c, "get_IsStatic" -> Helper.InstanceCall(c, "get_IsStatic", t, []) |> Some - | Some c, "get_IsGenericMethod" -> Helper.InstanceCall(c, "get_IsGenericMethod", t, []) |> Some - | Some c, "GetGenericMethodDefinition" -> Helper.InstanceCall(c, "GetGenericMethodDefinition", t, []) |> Some | Some c, "get_ReturnType" -> Helper.InstanceCall(c, "get_ReturnType", t, []) |> Some | Some c, "get_ReturnParameter" -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some | Some c, "GetFields" -> Helper.InstanceCall(c, "GetFields", t, []) |> Some diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 7c3086b040..7767dc1ba3 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -22,7 +22,7 @@ export abstract class NMemberInfo { constructor( public DeclaringType: NTypeInfo, public Name: string, - private attributes: CustomAttribute[], + public attributes: CustomAttribute[], ) {} public abstract toPrettyString(): string; @@ -58,25 +58,68 @@ export abstract class NMethodBase extends NMemberInfo { public get_IsStatic() { return this.IsStatic; } - public get_IsGenericMethod() { return false; } - - public GetGenericMethodDefinition() { return this; } - } export class NMethodInfo extends NMethodBase { public ReturnType: NTypeInfo; + private isGenericDef: boolean; + private genMap: { [name: string]: number } = {}; + constructor( DeclaringType: NTypeInfo, + public GenericArguments: NTypeInfo[], Name: string, Parameters: NParameterInfo[], returnType: NTypeInfo, IsStatic: boolean, private invoke: (...args: any[]) => any, attributes: CustomAttribute[], + private declaration?: NMethodInfo, ) { super(DeclaringType, Name, Parameters, IsStatic, attributes); - this.ReturnType = returnType.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(returnType) : returnType; + + const isDef = GenericArguments.findIndex((p) => p.isGenericParameter) >= 0; + this.isGenericDef = isDef; + if (isDef) { + GenericArguments.forEach((v, i) => { + this.genMap[v.fullname] = i; + }); + } else if (declaration) { + this.genMap = declaration.genMap; + } + + this.Parameters.forEach((p) => { p.ParameterType = this.ResolveGeneric(p.ParameterType); }); + this.ReturnType = returnType.get_ContainsGenericParameters() ? this.ResolveGeneric(returnType) : returnType; + } + + public ResolveGeneric(t: NTypeInfo): NTypeInfo { + if (t.isGenericParameter) { + return t.fullname in this.genMap ? this.GenericArguments[this.genMap[t.fullname]] : this.DeclaringType.ResolveGeneric(t); + } else if (t.get_ContainsGenericParameters()) { + const gen = t.generics.map((t) => this.ResolveGeneric(t)); + return t.MakeGenericType(gen); + // return new NTypeInfo(t.fullname, t.genericCount, t.isGenericParameter, t.generics.map((t) => this.ResolveType(t)), t.members, t.declaration) + } else { + return t; + } + } + + public get_IsGenericMethodDefinition() { + return this.isGenericDef; + } + + public MakeGenericMethod(types: NTypeInfo[]) { + return new NMethodInfo(this.DeclaringType, types, this.Name, this.Parameters, this.ReturnType, this.IsStatic, this.invoke, this.attributes, this); + } + + public get_IsGenericMethod() { return this.isGenericDef || this.declaration; } + + public GetGenericMethodDefinition(): NMethodInfo { + if (this.declaration) { + return this.declaration; + } else { + return this; + } } public get_ReturnType() { return this.ReturnType; } @@ -311,11 +354,11 @@ export class NTypeInfo { private static parameterCache: {[i: string]: NTypeInfo} = {}; public generics: NTypeInfo[] = null; + public declaration: NTypeInfo = null; private instantiations: {[i: string]: NTypeInfo} = {}; private mems: NMemberInfo[] = null; private genericMap: {[name: string]: number} = {}; - private declaration: NTypeInfo = null; constructor( public fullname: string, @@ -352,9 +395,6 @@ export class NTypeInfo { if (idx < 0 || idx >= this.generics.length) { throw new Error("cannot resolve: " + t.toFullString()); } return this.generics[idx]; } else { - - const mapping = Object.keys(this.genericMap).map((k) => k + ": " + this.genericMap[k]).join(", "); - throw new Error("cannot resolve " + t.fullname + " { " + mapping + " }"); return t; } } else if (t.genericCount > 0) { @@ -534,7 +574,7 @@ export function declareNType(fullname: string, generics: number, members: (self: if (fullname in typeCache) { gen = typeCache[fullname]; } else { - const pars = Array.from({ length: generics }, (_, i) => getGenericParamter("a" + i)); + const pars = Array.from({ length: generics }, (_, i) => getGenericParameter("a" + i)); const mems = members || ((_self, _gen) => []); gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => mems(s, pars)); @@ -543,7 +583,7 @@ export function declareNType(fullname: string, generics: number, members: (self: return gen; } -export function getGenericParamter(name: string) { +export function getGenericParameter(name: string) { return NTypeInfo.getParameter(name); } @@ -592,7 +632,7 @@ export function ntype(fullname: string, genericNames?: string[], generics?: NTyp const b = genericNames.findIndex((t) => !t); if (b >= 0) { throw new Error("bad hate occured"); } - const pars = genericNames.map((n) => getGenericParamter(n)); + const pars = genericNames.map((n) => getGenericParameter(n)); gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); typeCache[fullname] = gen; } @@ -615,7 +655,7 @@ export function tuple(...generics: NTypeInfo[]): NTypeInfo { const gen = declareNType(name, generics.length, (self, gen) => selectMany(gen, (t, i) => [ new NPropertyInfo(self, "Item" + i, t, false, false, []), - new NMethodInfo(self, "get_Item" + i, [], t, false, ((a) => a[i]), []), + new NMethodInfo(self, [], "get_Item" + i, [], t, false, ((a) => a[i]), []), ])); return gen.MakeGenericType(generics); } @@ -627,7 +667,7 @@ export function delegate(...generics: NTypeInfo[]): NTypeInfo { const ret = generics[generics.length - 1]; const args = generics.slice(0, generics.length - 1).map((t, i) => new NParameterInfo("arg" + i, t)); return [ - new NMethodInfo(self, "Invoke", args, ret, false, ((target, ...args) => target(...args)), []), + new NMethodInfo(self, [], "Invoke", args, ret, false, ((target, ...args) => target(...args)), []), ]; }); return gen.MakeGenericType(generics); @@ -637,7 +677,7 @@ export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { const name = "Microsoft.FSharp.Core.FSharpFunc`2"; const gen = declareNType(name, 2, (self, gen) => [ - new NMethodInfo(self, "Invoke", [new NParameterInfo("arg", gen[0])], gen[1], false, ((target, ...args) => target(...args)), []), + new NMethodInfo(self, [], "Invoke", [new NParameterInfo("arg", gen[0])], gen[1], false, ((target, ...args) => target(...args)), []), ]); return gen.MakeGenericType([argType, returnType]); } @@ -655,7 +695,7 @@ export function option(generic?: NTypeInfo): NTypeInfo { export function list(generic?: NTypeInfo): NTypeInfo { const gen = declareNType("Microsoft.FSharp.Collections.FSharpList`1", 1, (self, gen) => [ new NPropertyInfo(self, "Head", gen[0], false, false, []), - new NMethodInfo(self, "get_Head", [], gen[0], false, ((l) => l[0]), []), + new NMethodInfo(self, [], "get_Head", [], gen[0], false, ((l) => l[0]), []), ]); return gen.MakeGenericType([generic]); // return new NTypeInfo("Microsoft.FSharp.Collections.FSharpList`1", 1, false, [generic], (_s) => []); @@ -664,10 +704,10 @@ export function list(generic?: NTypeInfo): NTypeInfo { export function array(generic?: NTypeInfo): NTypeInfo { const gen = declareNType("_[]", 1, (self, gen) => [ new NPropertyInfo(self, "Length", int32, false, false, []), - new NMethodInfo(self, "get_Length", [], int32, false, ((l) => l.length), []), + new NMethodInfo(self, [], "get_Length", [], int32, false, ((l) => l.length), []), ]); return gen.MakeGenericType([generic]); - // generic = generic || getGenericParamter("a"); + // generic = generic || getGenericParameter("a"); // return new NTypeInfo(generic.fullname + "[]", 1, false, [generic], (_s) => []); } diff --git a/tests/Main/ReflectionTests.fs b/tests/Main/ReflectionTests.fs index 03e5089b48..c02db8f451 100644 --- a/tests/Main/ReflectionTests.fs +++ b/tests/Main/ReflectionTests.fs @@ -409,7 +409,17 @@ type MyRecord20 = { FieldA: int FieldB: string } +type MyClass<'a>() = + static member GetValue<'b>(a : 'a, b : 'b) = (a,b) + + let fableTests = [ + testCase "Generic Methods" <| fun () -> + let tm = typedefof>.MakeGenericType [| typeof |] + let meth = tm.GetMethods().[0] + let m = meth.MakeGenericMethod [| typeof |] + m.GetParameters() |> failwithf "%A" + testCase "ITypeResolver can be injected" <| fun () -> let x: R1 = Helper.Make [|box 5|] let y: R2 = Helper.Make [|box 10|] From 0a0cbd4edbfede80b0a864d764f3092460578a65 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 9 Apr 2019 11:27:01 +0200 Subject: [PATCH 10/38] Reflection error messages --- src/Fable.Transforms/Fable2Babel.fs | 5 +- src/Fable.Transforms/Replacements.fs | 45 ++++++-- src/fable-library/Reflection.ts | 163 +++++++++++++++++++-------- tests/Main/ReflectionTests.fs | 35 +++++- 4 files changed, 187 insertions(+), 61 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 00312cfbea..5e8f1c6684 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -515,9 +515,8 @@ module Util = let resolveGenerics generics: Expression[] = generics |> Array.map (transformTypeInfo com ctx r [||] genMap) let genericTypeInfo name genArgs = - let gen = genArgs |> Array.exists (function Fable.GenericParam _ -> true | _ -> false) - let resolved = if gen then [||] else resolveGenerics genArgs - coreLibCall com ctx None "Reflection" name resolved + let gen = genArgs |> Array.map (function Fable.GenericParam n -> coreLibCall com ctx None "Reflection" "getGenericParameter" [|StringLiteral n|] | t -> transformTypeInfo com ctx r [||] genMap t) + coreLibCall com ctx None "Reflection" name gen let genericEntity (ent: FSharpEntity) generics = let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index ef3eb64f44..071cd1ebd7 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -336,9 +336,9 @@ let makeTypeInfo r t = let makeTypeDefinitionInfo r t = let t = match t with - | Option _ -> Option (GenericParam "a") - | Array _ -> Array (GenericParam "a") - | List _ -> List (GenericParam "a") + | Option _ -> Option (GenericParam "a0") + | Array _ -> Array (GenericParam "a0") + | List _ -> List (GenericParam "a0") | Tuple genArgs -> genArgs |> List.mapi (fun i _ -> (GenericParam (sprintf "a%d" i))) |> Tuple | DeclaredType(ent, genArgs) -> @@ -346,11 +346,11 @@ let makeTypeDefinitionInfo r t = let genArgs = names |> List.map GenericParam DeclaredType(ent, genArgs) | FunctionType(kind,r) -> - let kind = + let kind, ret = match kind with - | LambdaType(a) -> LambdaType (GenericParam "a") - | DelegateType(a) -> a |> List.mapi (fun i _ -> GenericParam (sprintf "a%d" i)) |> DelegateType - FunctionType(kind, GenericParam "b") + | LambdaType(a) -> LambdaType (GenericParam "a0"), "a1" + | DelegateType(a) -> DelegateType (a |> List.mapi (fun i _ -> GenericParam (sprintf "a%d" i))), sprintf "a%d" a.Length + FunctionType(kind, GenericParam ret) | ErasedUnion a -> ErasedUnion a // TODO: Do something with FunctionType and ErasedUnion? | t -> t @@ -2618,7 +2618,13 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some | Some this, "GetTypeInfo" -> Some this | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some - | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t args |> Some + | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t [] |> Some + | Some this, "GetMethod" -> + match args with + | [a] when a.Type = Fable.String -> callTypeInfoMethod this "GetMethod" t [a] |> Some + | a::b::_ when a.Type = Fable.String && b.Type = Fable.Array (Fable.MetaType) -> callTypeInfoMethod this "GetMethod" t [a; b] |> Some + | a::_::_::b::_ when a.Type = Fable.String && b.Type = Fable.Array (Fable.MetaType) -> callTypeInfoMethod this "GetMethod" t [a; b] |> Some + | _ -> None | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some @@ -2842,6 +2848,12 @@ let private replacedModules = "System.Reflection.TypeInfo", types ] + +let reflectionMethods = + System.Collections.Generic.HashSet [| + "asdads" + |] + let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr option) (args: Expr list) = match info.DeclaringEntityFullName with | Patterns.DicContains replacedModules replacement -> replacement com ctx r t info thisArg args @@ -2869,6 +2881,7 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | "System.Reflection.MethodBase" | "System.Reflection.ConstructorInfo" | "System.Reflection.PropertyInfo" + | "System.Reflection.ParameterInfo" | "System.Reflection.FieldInfo" | "Microsoft.FSharp.Reflection.UnionCaseInfo" | "FSharp.Reflection.UnionCaseInfo" @@ -2890,6 +2903,22 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | Some c, "get_ReturnParameter" -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some | Some c, "GetFields" -> Helper.InstanceCall(c, "GetFields", t, []) |> Some | Some c, "get_PropertyType" -> Helper.InstanceCall(c, "get_PropertyType", t, []) |> Some + | Some c, "get_ParameterType" -> Helper.InstanceCall(c, "get_ParameterType", t, []) |> Some + | Some c, "get_GetMethod" -> Helper.InstanceCall(c, "get_GetMethod", t, []) |> Some + | Some c, "get_SetMethod" -> Helper.InstanceCall(c, "get_SetMethod", t, []) |> Some + + | Some c, "SetValue" -> + match args with + | [a;v] -> Helper.InstanceCall(c, "SetValue", t, [a; v]) |> Some + | [a;v;i] -> Helper.InstanceCall(c, "SetValue", t, [a; v; i]) |> Some + | a::v::_::_::i::_ -> Helper.InstanceCall(c, "SetValue", t, [a; v; i]) |> Some + | _ -> None + | Some c, "GetValue" -> + match args with + | [a] -> Helper.InstanceCall(c, "GetValue", t, [a]) |> Some + | [a;i] -> Helper.InstanceCall(c, "GetValue", t, [a; i]) |> Some + | a::_::_::i::_ -> Helper.InstanceCall(c, "GetValue", t, [a; i]) |> Some + | _ -> None | _ -> let isStatic = Option.isNone thisArg sprintf "UNKNOWN reflection member: { isStatic: %A; name: %A; args: %A }" isStatic info.CompiledName args |> addError com [] None diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 7767dc1ba3..5fa7c86e32 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1,4 +1,4 @@ -import { Record, Union, declare } from "./Types"; +import { Record, Union } from "./Types"; import { compareArraysWith, equalArraysWith, stringHash } from "./Util"; // tslint:disable: max-line-length @@ -16,14 +16,30 @@ export class NParameterInfo { public toString() { return this.ParameterType.toString() + " " + this.Name; } + + public get_ParameterType() { + return this.ParameterType; + } + + public get_Name() { + return this.Name; + } + } export abstract class NMemberInfo { + public attributes: CustomAttribute[]; + constructor( public DeclaringType: NTypeInfo, public Name: string, - public attributes: CustomAttribute[], - ) {} + att: CustomAttribute[], + ) { + + if (!Name) { throw new Error(`cannot declare MemberInfo without a name`); } + if (!DeclaringType) { throw new Error(`${Name} has no declaring type`); } + this.attributes = att || []; + } public abstract toPrettyString(): string; public get_Name() { return this.Name; } @@ -43,14 +59,18 @@ export abstract class NMemberInfo { export abstract class NMethodBase extends NMemberInfo { public Parameters: NParameterInfo[]; + public IsStatic: boolean; + constructor( DeclaringType: NTypeInfo, Name: string, parameters: NParameterInfo[], - public IsStatic: boolean, + isStatic: boolean, attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); + parameters = parameters || []; + this.IsStatic = isStatic || false; this.Parameters = parameters.map((t) => t.ParameterType.get_ContainsGenericParameters() ? new NParameterInfo(t.Name, DeclaringType.ResolveGeneric(t.ParameterType)) : t); } @@ -58,36 +78,56 @@ export abstract class NMethodBase extends NMemberInfo { public get_IsStatic() { return this.IsStatic; } + public ParametersAssignable(ts: NTypeInfo[]) { + if (this.Parameters.length === ts.length) { + const broken = + this.Parameters.findIndex((p, i) => { + const d = p.ParameterType; + const a = ts[i]; + return !(d.isGenericParameter || d.fullname === "System.Object" || equals(d, a)); + }); + return broken < 0; + } else { + return false; + } + } + } export class NMethodInfo extends NMethodBase { public ReturnType: NTypeInfo; + public GenericArguments: NTypeInfo[]; + private isGenericDef: boolean; private genMap: { [name: string]: number } = {}; constructor( - DeclaringType: NTypeInfo, - public GenericArguments: NTypeInfo[], - Name: string, - Parameters: NParameterInfo[], + declaringType: NTypeInfo, + genericArguments: NTypeInfo[], + name: string, + parameters: NParameterInfo[], returnType: NTypeInfo, - IsStatic: boolean, + isStatic: boolean, private invoke: (...args: any[]) => any, attributes: CustomAttribute[], private declaration?: NMethodInfo, ) { - super(DeclaringType, Name, Parameters, IsStatic, attributes); + super(declaringType, name, parameters, isStatic, attributes); + + if (!returnType) { throw new Error(`MethodInfo ${name} does not have a return type`); } + + genericArguments = genericArguments || []; + const isDef = genericArguments.findIndex((p) => p.isGenericParameter) >= 0; - const isDef = GenericArguments.findIndex((p) => p.isGenericParameter) >= 0; + this.GenericArguments = genericArguments; this.isGenericDef = isDef; if (isDef) { - GenericArguments.forEach((v, i) => { + genericArguments.forEach((v, i) => { this.genMap[v.fullname] = i; }); } else if (declaration) { this.genMap = declaration.genMap; } - this.Parameters.forEach((p) => { p.ParameterType = this.ResolveGeneric(p.ParameterType); }); this.ReturnType = returnType.get_ContainsGenericParameters() ? this.ResolveGeneric(returnType) : returnType; } @@ -146,9 +186,10 @@ export class NMethodInfo extends NMethodBase { return this.toPrettyString(); } - public Invoke(target: any, ...args: any[]) { + public Invoke(target: any, args: any[]) { + args = args || []; if (!this.IsStatic) { - const a = [target, ...args]; + if (!target) { throw new Error(`MethodInfo ${this.toPrettyString()} cannot be called without a this argument`); } return this.invoke.apply(null, [target, ...args]); } else { return this.invoke.apply(null, args); @@ -158,12 +199,12 @@ export class NMethodInfo extends NMethodBase { export class NConstructorInfo extends NMethodBase { constructor( - DeclaringType: NTypeInfo, - Parameters: NParameterInfo[], + declaringType: NTypeInfo, + parameters: NParameterInfo[], private invoke: (...args: any[]) => any, attributes: CustomAttribute[], ) { - super(DeclaringType, ".ctor", Parameters, true, attributes); + super(declaringType, ".ctor", parameters, true, attributes); } public toPrettyString() { @@ -182,22 +223,26 @@ export class NConstructorInfo extends NMethodBase { return this.toPrettyString(); } - public Invoke(...args: any[]) { - return this.invoke.apply(null, args); + public Invoke(args: any[]) { + args = args || []; + return this.invoke.apply(null, args); } } export class NFieldInfo extends NMemberInfo { public Type: NTypeInfo = null; constructor( - DeclaringType: NTypeInfo, - Name: string, + declaringType: NTypeInfo, + name: string, type: NTypeInfo, public IsStatic: boolean, attributes: CustomAttribute[], ) { - super(DeclaringType, Name, attributes); - this.Type = type.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(type) : type; + super(declaringType, name, attributes); + + if (!type) { throw new Error(`FieldInfo ${name} does not have a type`); } + + this.Type = type.get_ContainsGenericParameters() ? this.DeclaringType.ResolveGeneric(type) : type; } public get_FieldType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } @@ -233,6 +278,9 @@ export class NPropertyInfo extends NMemberInfo { attributes: CustomAttribute[], ) { super(DeclaringType, Name, attributes); + + if (!type) { throw new Error(`FieldInfo ${Name} does not have a type`); } + this.Type = type.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(type) : type; } public get_PropertyType() { return this.Type; } @@ -253,14 +301,30 @@ export class NPropertyInfo extends NMemberInfo { if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } } - public GetValue(target: any, ...index: any[]) { - const g = this.get_GetMethod(); - return g.Invoke(target, index); + public GetValue(target: any, index: any[]): any { + if (this.IsFSharp) { + // TODO: mangled-names???? + if (this.Name in target) { + return target[this.Name]; + } else { + throw new Error(`property ${this.Name} not in target`); + } + } else { + const g = this.get_GetMethod(); + if (g === null) { throw new Error(`property ${this.Name} has no GetMethod`); } + index = index || []; + return g.Invoke(target, index); + } } - public SetValue(target: any, value: any, ...index: any[]) { - const s = this.get_SetMethod(); - s.Invoke(target, [...index, value]); + public SetValue(target: any, value: any, index: any[]) { + if (this.IsFSharp) { + target[this.Name] = value; + } else { + index = index || []; + const s = this.get_SetMethod(); + s.Invoke(target, [...index, value]); + } } public toPrettyString() { @@ -314,6 +378,10 @@ export class NUnionCaseInfo extends NMemberInfo { public Invoke: (...args: any[]) => any, ) { super(DeclaringType, Name, Attributes); + + if (typeof Tag !== "number") { throw new Error(`UnionCase ${Name} does not have a tag`); } + + fields = fields || []; this.Fields = fields.map ((tup) => tup[1].get_ContainsGenericParameters() ? [tup[0], DeclaringType.ResolveGeneric(tup[1])] : tup) as Array<[string, NTypeInfo]>; } @@ -367,6 +435,12 @@ export class NTypeInfo { _generics: NTypeInfo[], public members: (self: NTypeInfo) => NMemberInfo[], decl?: NTypeInfo) { + if (!fullname) { throw new Error("cannot declare type without name"); } + members = members || ((_s) => []); + _generics = _generics || []; + isGenericParameter = isGenericParameter || false; + genericCount = genericCount || 0; + const g = _generics.filter((t) => !t.isGenericParameter); if (g.length === genericCount) { this.generics = g; @@ -379,20 +453,14 @@ export class NTypeInfo { }); this.generics = _generics; } - if (this.generics.length !== this.genericCount) { throw new Error("invalid generic count"); } + if (this.generics.length !== this.genericCount) { throw new Error(`${this.fullname} contains ${this.genericCount} generic parameters but only ${this.generics.length} given.`); } this.declaration = decl || null; - - if (fullname === "Microsoft.FSharp.Collections.FSharpList`1" && this.generics.length === 0) { - throw new Error("bad list type"); - } - } public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genericMap) { const idx = this.genericMap[t.fullname]; - if (idx < 0 || idx >= this.generics.length) { throw new Error("cannot resolve: " + t.toFullString()); } return this.generics[idx]; } else { return t; @@ -410,15 +478,14 @@ export class NTypeInfo { } else if (this.declaration) { return this.declaration; } else { - throw new Error("bad HATE"); - return new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, [], this.members); + throw new Error(`${this.fullname} does not have a proper generic definition`); } } public MakeGenericType(args: NTypeInfo[]) { args = args.filter((a) => a); if (args.length === 0) { return this; } - if (args.length !== this.genericCount) { throw new Error("invalid generic argument count"); } + if (args.length !== this.genericCount) { throw new Error(`${this.fullname} contains ${this.genericCount} generic parameters but only ${this.generics.length} given.`); } const key = args.map((t) => t.toString()).join(", "); if (key in this.instantiations) { @@ -512,10 +579,15 @@ export class NTypeInfo { const prop = m.find((m) => m instanceof NPropertyInfo && m.Name === name) as NPropertyInfo; return prop; } - public GetMethod(name: string) { + public GetMethod(name: string, types?: NTypeInfo[]) { const m = this.GetAllMembers(); - const meth = m.find((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo; - return meth; + if (types) { + const meths = m.filter((m) => m instanceof NMethodInfo && m.Name === name && m.ParametersAssignable(types)) as NMethodInfo[]; + return meths.length === 1 ? meths[0] : null; + } else { + const meths = m.filter((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo[]; + return meths.length === 1 ? meths[0] : null; + } } public toFullString(): string { if (this.isGenericParameter) { @@ -651,6 +723,7 @@ function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) = } export function tuple(...generics: NTypeInfo[]): NTypeInfo { + if (generics.length === 0) { throw new Error("empty tuple"); } const name = "System.Tuple`" + generics.length; const gen = declareNType(name, generics.length, (self, gen) => selectMany(gen, (t, i) => [ @@ -663,7 +736,7 @@ export function tuple(...generics: NTypeInfo[]): NTypeInfo { export function delegate(...generics: NTypeInfo[]): NTypeInfo { const name = "System.Func`" + generics.length; const gen = - declareNType(name, generics.length, (self, gen) => { + declareNType(name, generics.length, (self, _gen) => { const ret = generics[generics.length - 1]; const args = generics.slice(0, generics.length - 1).map((t, i) => new NParameterInfo("arg" + i, t)); return [ @@ -881,7 +954,7 @@ export function makeRecord(t: NTypeInfo, values: any[]): any { throw new Error(`Expected an array of length ${fields.length} but got ${values.length}`); } const ctor = t.GetAllMembers().find((m) => m instanceof NConstructorInfo) as NConstructorInfo; - return ctor.Invoke(...values); + return ctor.Invoke(values); } export function makeTuple(values: any[], t: NTypeInfo): any { diff --git a/tests/Main/ReflectionTests.fs b/tests/Main/ReflectionTests.fs index c02db8f451..a572d571fe 100644 --- a/tests/Main/ReflectionTests.fs +++ b/tests/Main/ReflectionTests.fs @@ -409,16 +409,41 @@ type MyRecord20 = { FieldA: int FieldB: string } -type MyClass<'a>() = +type MyClass<'a>(value : 'a) = static member GetValue<'b>(a : 'a, b : 'b) = (a,b) - + member x.Value : 'a = value let fableTests = [ - testCase "Generic Methods" <| fun () -> + testCase "Generic static method can be instantiated and invoked" <| fun () -> let tm = typedefof>.MakeGenericType [| typeof |] - let meth = tm.GetMethods().[0] + let meth = tm.GetMethod("GetValue") let m = meth.MakeGenericMethod [| typeof |] - m.GetParameters() |> failwithf "%A" + let pars = m.GetParameters() |> Array.map (fun p -> p.ParameterType, p.Name) + let res = m.Invoke(null, [| 1 :> obj; 2.0 :> obj |]) |> unbox + + res |> equal (1, 2.0) + m.ReturnType |> equal typeof + pars |> equal [| (typeof, "a"); (typeof, "b") |] + + + testCase "Property can be read" <| fun () -> + let tm = typedefof>.MakeGenericType [| typeof |] + let prop = tm.GetProperty("Value") + let v1 = prop.GetMethod.Invoke(MyClass(1), null) |> unbox + let v2 = prop.GetValue(MyClass(1)) |> unbox + v1 |> equal 1 + v2 |> equal 1 + + + testCase "Record Property can be read" <| fun () -> + let tm = typeof + let fa = tm.GetProperty("FieldA") + let fb = tm.GetProperty("FieldB") + let r = { FieldA = 10; FieldB = "asdasd"} + let va = fa.GetValue(r) |> unbox + let vb = fb.GetValue(r) |> unbox + va |> equal 10 + vb |> equal "asdasd" testCase "ITypeResolver can be injected" <| fun () -> let x: R1 = Helper.Make [|box 5|] From eefedb5bc564f0b5b01ca7ff1a818d6b3662a7cb Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 9 Apr 2019 15:44:50 +0200 Subject: [PATCH 11/38] Expr implementation --- src/Fable.Transforms/Replacements.fs | 1 + src/fable-library/Quotations.fs | 529 ++++++++++++++++++++++++++- src/fable-library/Reflection.ts | 4 + tests/Main/DateTimeOffsetTests.fs | 10 +- tests/Main/DateTimeTests.fs | 10 +- tests/Main/ExprTests.fs | 6 +- tests/Main/Fable.Tests.fsproj | 1 + tests/Main/Main.fs | 1 + 8 files changed, 540 insertions(+), 22 deletions(-) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 071cd1ebd7..cb260ca3a6 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2616,6 +2616,7 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some + | Some this, "MakeArrayType" -> callTypeInfoMethod this "MakeArrayType" t [] |> Some | Some this, "GetTypeInfo" -> Some this | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t [] |> Some diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 4f8205d7b5..5acf8733d4 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -70,6 +70,36 @@ type ExprConstInfo = | ValueOp of obj * System.Type * Option | IfThenElseOp | NewRecordOp of System.Type + | NewUnionCaseOp of UnionCaseInfo + | UnionCaseTestOp of UnionCaseInfo + | NewTupleOp of System.Type + | TupleGetOp of System.Type * int + | InstancePropGetOp of System.Reflection.PropertyInfo + | StaticPropGetOp of System.Reflection.PropertyInfo + | InstancePropSetOp of System.Reflection.PropertyInfo + | StaticPropSetOp of System.Reflection.PropertyInfo + | InstanceFieldGetOp of System.Reflection.FieldInfo + | StaticFieldGetOp of System.Reflection.FieldInfo + | InstanceFieldSetOp of System.Reflection.FieldInfo + | StaticFieldSetOp of System.Reflection.FieldInfo + | NewObjectOp of System.Reflection.ConstructorInfo + | InstanceMethodCallOp of System.Reflection.MethodInfo + | StaticMethodCallOp of System.Reflection.MethodInfo + | CoerceOp of System.Type + | NewArrayOp of System.Type + | NewDelegateOp of System.Type * int + | QuoteOp of bool + | SequentialOp + | AddressOfOp + | VarSetOp + | AddressSetOp + | TypeTestOp of System.Type + | TryWithOp + | TryFinallyOp + | ForIntegerRangeLoopOp + | WhileLoopOp + | WithValueOp of obj * System.Type + | DefaultValueOp of System.Type and Tree = | CombTerm of ExprConstInfo * Expr list @@ -97,12 +127,46 @@ and [ t | IfThenElseOp, L3(_,i,_) -> typeOf i - | LetRecOp, [IteratedLambda(_, E(CombTerm(LetRecCombOp, b2::_)))] -> typeOf b2 - | LetRecOp, _ -> failwith "bad" - | LetRecCombOp, _ -> failwith "bad" - - | NewRecordOp t, _ -> t - + | LetRecOp, L1(IteratedLambda(_, E(CombTerm(LetRecCombOp, b2::_)))) -> typeOf b2 + | LetRecOp, _ -> failwith "internal error" + | LetRecCombOp, _ -> failwith "internal error" + + | NewRecordOp t, _ -> t + | NewUnionCaseOp c, _ -> c.DeclaringType + | UnionCaseTestOp _, _ -> typeof + | NewTupleOp t, _ -> t + | TupleGetOp(t, i), _ -> FSharpType.GetTupleElements(t).[i] + + | InstancePropGetOp p, _ -> p.PropertyType + | StaticPropGetOp p, _ -> p.PropertyType + | InstanceFieldGetOp p, _ -> p.FieldType + | StaticFieldGetOp p, _ -> p.FieldType + + | InstancePropSetOp _, _ -> typeof + | StaticPropSetOp _, _ -> typeof + | InstanceFieldSetOp _, _ -> typeof + | StaticFieldSetOp _, _ -> typeof + + | NewObjectOp c, _ -> c.DeclaringType + | InstanceMethodCallOp m, _ -> m.ReturnType + | StaticMethodCallOp m, _ -> m.ReturnType + + | CoerceOp t, _ -> t + | NewArrayOp t, _ -> t.MakeArrayType() + | NewDelegateOp(t,_), _ -> t + + | QuoteOp _, _ -> typeof + | SequentialOp, L2(_,r) -> r.Type + | AddressOfOp, L1(e) -> e.Type + | VarSetOp, _ -> typeof + | AddressSetOp, _ -> typeof + | TypeTestOp _, _ -> typeof + | TryWithOp, L3(t,_,_) -> t.Type + | TryFinallyOp, L2(t,_) -> t.Type + | ForIntegerRangeLoopOp, _ -> typeof + | WhileLoopOp, _ -> typeof + | WithValueOp(_,t), _ -> t + | DefaultValueOp t, _ -> t member internal x.Tree = tree @@ -153,11 +217,330 @@ and [) = Expr(CombTerm(NewRecordOp t, args)) - -[] + static member NewUnionCase(t : UnionCaseInfo, args : list) = + Expr(CombTerm(NewUnionCaseOp t, args)) + + static member UnionCaseTest(t : UnionCaseInfo, arg : Expr) = + Expr(CombTerm(UnionCaseTestOp t, [arg])) + + static member NewTuple(t : System.Type, args : list) = + Expr(CombTerm(NewTupleOp t, args)) + + static member TupleGet(t : System.Type, n : int, self : Expr) = + Expr(CombTerm(TupleGetOp(t,n), [self])) + + static member PropertyGet(obj : Expr, property : System.Reflection.PropertyInfo, ?indexerArgs) = + let indexerArgs = defaultArg indexerArgs [] + Expr(CombTerm(InstancePropGetOp(property), obj :: indexerArgs)) + + static member PropertyGet(property : System.Reflection.PropertyInfo, ?indexerArgs) = + let indexerArgs = defaultArg indexerArgs [] + Expr(CombTerm(StaticPropGetOp(property), indexerArgs)) + + static member FieldGet(obj : Expr, field : System.Reflection.FieldInfo) = + Expr(CombTerm(InstanceFieldGetOp(field), [obj])) + + static member FieldGet(field : System.Reflection.FieldInfo) = + Expr(CombTerm(StaticFieldGetOp(field), [])) + + + static member PropertySet(obj : Expr, property : System.Reflection.PropertyInfo, value : Expr, ?indexerArgs) = + let indexerArgs = defaultArg indexerArgs [] + Expr(CombTerm(InstancePropSetOp(property), obj :: (indexerArgs @ [value]))) + + static member PropertySet(property : System.Reflection.PropertyInfo, value : Expr, ?indexerArgs) = + let indexerArgs = defaultArg indexerArgs [] + Expr(CombTerm(StaticPropSetOp(property), indexerArgs @ [value])) + + static member FieldSet(obj : Expr, field : System.Reflection.FieldInfo, value : Expr) = + Expr(CombTerm(InstanceFieldSetOp(field), [obj;value])) + + static member FieldSet(field : System.Reflection.FieldInfo, value : Expr) = + Expr(CombTerm(StaticFieldSetOp(field), [value])) + + static member Coerce(source : Expr, target : System.Type) = + Expr(CombTerm(CoerceOp(target), [source])) + static member NewArray(elementType : System.Type, elements : list) = + Expr(CombTerm(NewArrayOp(elementType), elements)) + + static member NewDelegate (delegateType : System.Type, parameters: Var list, body: Expr) = + Expr(CombTerm(NewDelegateOp(delegateType, parameters.Length), [Helpers.mkRLinear Expr.Lambda (parameters, body)])) + + static member Quote(e : Expr) = + Expr(CombTerm(QuoteOp true, [e])) + static member QuoteRaw(e : Expr) = + Expr(CombTerm(QuoteOp false, [e])) + static member QuoteTyped(e : Expr) = + Expr(CombTerm(QuoteOp false, [e])) + static member Sequential(l : Expr, r : Expr) = + Expr(CombTerm(SequentialOp, [l;r])) + static member TryWith(body : Expr, filterVar : Var, filterBody : Expr, catchVar : Var, catchExpr : Expr) = + Expr(CombTerm(TryWithOp, [body; Expr.Lambda(filterVar, filterBody); Expr.Lambda(catchVar, catchExpr)])) + static member TryFinally(body : Expr, compensation : Expr) = + Expr(CombTerm(TryFinallyOp, [body; compensation])) + static member ForIntegerRangeLoop(i : Var, s : Expr, e : Expr, body : Expr) = + Expr(CombTerm(ForIntegerRangeLoopOp, [s;e;Expr.Lambda(i, body)])) + static member WhileLoop(cond : Expr, body : Expr) = + Expr(CombTerm(WhileLoopOp, [cond; body])) + static member VarSet(v : Var, value : Expr) = + Expr (CombTerm(VarSetOp,[Expr.Var v; value])) + static member AddressSet(addr : Expr, value : Expr) = + Expr (CombTerm(AddressSetOp,[addr; value])) + static member TypeTest(source : Expr, target : System.Type) = + Expr (CombTerm(TypeTestOp target,[source])) + static member DefaultValue(t : System.Type) = + Expr (CombTerm(DefaultValueOp t, [])) + static member WithValue(value: obj, expressionType: System.Type, definition: Expr) = + Expr (CombTerm(WithValueOp(value, expressionType), [definition])) + + static member Call (methodInfo:System.Reflection.MethodInfo, arguments) = + Expr (CombTerm(StaticMethodCallOp(methodInfo), arguments)) + + static member Call (obj:Expr, methodInfo:System.Reflection.MethodInfo, arguments) = + Expr (CombTerm(InstanceMethodCallOp(methodInfo), obj::arguments)) + + static member NewObject (constructorInfo:System.Reflection.ConstructorInfo, arguments) = + Expr (CombTerm(NewObjectOp constructorInfo, arguments)) + +[] module Patterns = + let E t = Expr(t) + let EA (t, attribs) = Expr(t) + let ES ts = List.map E ts + + let (|E|) (e: Expr) = e.Tree + let (|ES|) (es: list) = 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 voidTy = typeof + let unitTy = typeof + 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:System.Type) = t.MakeArrayType() + let rawExprTy = typeof + let mkExprTy (t:System.Type) = rawExprTy + + + //-------------------------------------------------------------------------- + // 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 + + [] + let (|Var|_|) (E x) = match x with VarTerm v -> Some v | _ -> None + + [] + let (|Application|_|) input = match input with Comb2(AppOp, a, b) -> Some (a, b) | _ -> None + + [] + let (|Lambda|_|) (E x) = match x with LambdaTerm(a, b) -> Some (a, b) | _ -> None + + [] + let (|Quote|_|) (E x) = match x with CombTerm(QuoteOp _, [a]) -> Some (a) | _ -> None + + [] + let (|QuoteRaw|_|) (E x) = match x with CombTerm(QuoteOp false, [a]) -> Some (a) | _ -> None + + [] + let (|QuoteTyped|_|) (E x) = match x with CombTerm(QuoteOp true, [a]) -> Some (a) | _ -> None + + [] + let (|IfThenElse|_|) input = match input with Comb3(IfThenElseOp, e1, e2, e3) -> Some(e1, e2, e3) | _ -> None + + [] + let (|NewTuple|_|) input = match input with E(CombTerm(NewTupleOp(_), es)) -> Some(es) | _ -> None + + [] + let (|DefaultValue|_|) input = match input with E(CombTerm(DefaultValueOp(ty), [])) -> Some(ty) | _ -> None + + [] + let (|NewRecord|_|) input = match input with E(CombTerm(NewRecordOp(x), es)) -> Some(x, es) | _ -> None + + [] + let (|NewUnionCase|_|) input = match input with E(CombTerm(NewUnionCaseOp(unionCase), es)) -> Some(unionCase, es) | _ -> None + + [] + let (|UnionCaseTest|_|) input = match input with Comb1(UnionCaseTestOp(unionCase), e) -> Some(e, unionCase) | _ -> None + + [] + let (|TupleGet|_|) input = match input with Comb1(TupleGetOp(_, n), e) -> Some(e, n) | _ -> None + + [] + let (|Coerce|_|) input = match input with Comb1(CoerceOp ty, e1) -> Some(e1, ty) | _ -> None + + [] + let (|TypeTest|_|) input = match input with Comb1(TypeTestOp ty, e1) -> Some(e1, ty) | _ -> None + + [] + let (|NewArray|_|) input = match input with E(CombTerm(NewArrayOp ty, es)) -> Some(ty, es) | _ -> None + + [] + let (|AddressSet|_|) input = match input with E(CombTerm(AddressSetOp, [e;v])) -> Some(e, v) | _ -> None + + [] + let (|TryFinally|_|) input = match input with E(CombTerm(TryFinallyOp, [e1;e2])) -> Some(e1, e2) | _ -> None + + [] + let (|TryWith|_|) input = match input with E(CombTerm(TryWithOp, [e1;Lambda(v1, e2);Lambda(v2, e3)])) -> Some(e1, v1, e2, v2, e3) | _ -> None + + [] + let (|VarSet|_| ) input = match input with E(CombTerm(VarSetOp, [E(VarTerm(v)); e])) -> Some(v, e) | _ -> None + + [] + let (|Value|_|) input = match input with E(CombTerm(ValueOp (v, ty, _), _)) -> Some(v, ty) | _ -> None + + [] + let (|ValueObj|_|) input = match input with E(CombTerm(ValueOp (v, _, _), _)) -> Some(v) | _ -> None + + [] + let (|ValueWithName|_|) input = + match input with + | E(CombTerm(ValueOp (v, ty, Some nm), _)) -> Some(v, ty, nm) + | _ -> None + + [] + let (|WithValue|_|) input = + match input with + | E(CombTerm(WithValueOp (v, ty), [e])) -> Some(v, ty, e) + | _ -> None + + [] + let (|AddressOf|_|) input = + match input with + | Comb1(AddressOfOp, e) -> Some(e) + | _ -> None + + [] + let (|Sequential|_|) input = + match input with + | Comb2(SequentialOp, e1, e2) -> Some(e1, e2) + | _ -> None + + [] + let (|ForIntegerRangeLoop|_|) input = + match input with + | Comb3(ForIntegerRangeLoopOp, e1, e2, Lambda(v, e3)) -> Some(v, e1, e2, e3) + | _ -> None + + [] + let (|WhileLoop|_|) input = + match input with + | Comb2(WhileLoopOp, e1, e2) -> Some(e1, e2) + | _ -> None + + [] + 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 + + [] + 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 + + + [] + let (|FieldGet|_|) input = + match input with + | E(CombTerm(StaticFieldGetOp finfo, [])) -> Some(None, finfo) + | E(CombTerm(InstanceFieldGetOp finfo, [obj])) -> Some(Some(obj), finfo) + | _ -> None + + [] + 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 + + [] + let (|NewObject|_|) input = + match input with + | E(CombTerm(NewObjectOp ty, e)) -> Some(ty, e) | _ -> None + + [] + 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 + + [] + let (|Let|_|)input = + match input with + | LetRaw(e, Lambda(v, body)) -> Some(v, e, body) + | _ -> None + + let (|IteratedLambda|_|) (e: Expr) = Helpers.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 + + [] + let (|NewDelegate|_|) input = + match input with + | Comb1(NewDelegateOp(ty, nargs), e) -> + 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 + + [] + let (|LetRecursive|_|) input = + match input with + | LetRecRaw(IteratedLambda(vs1, E(CombTerm(LetRecCombOp, body::es)))) -> Some(List.zip vs1 es, body) + | _ -> None + + + + + +module PatternsOld = + let inline (|E|) (e : Expr) = 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 (|Value|_|) (e : Expr) = @@ -211,4 +594,132 @@ module Patterns = let (|NewRecord|_|) (e : Expr) = match e with | E(CombTerm(NewRecordOp t, args)) -> Some (t, args) - | _ -> None \ No newline at end of file + | _ -> None + + [] + let (|NewUnionCase|_|) (e : Expr) = + match e with + | E(CombTerm(NewUnionCaseOp c, args)) -> Some (c, args) + | _ -> None + + [] + let (|UnionCaseTest|_|) (e : Expr) = + match e with + | E(CombTerm(UnionCaseTestOp c, [arg])) -> Some (arg, c) + | _ -> None + + [] + let (|NewTuple|_|) (e : Expr) = + match e with + | E(CombTerm(NewTupleOp _, args)) -> Some(args) + | _ -> None + + [] + let (|TupleGet|_|) (e : Expr) = + match e with + | E(CombTerm(TupleGetOp(_, i), [arg])) -> Some(arg, i) + | _ -> None + + + + [] + 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 + + [] + let (|FieldGet|_|) input = + match input with + | E(CombTerm(StaticFieldGetOp finfo, [])) -> Some(None, finfo) + | E(CombTerm(InstanceFieldGetOp finfo, [obj])) -> Some(Some(obj), finfo) + | _ -> None + + + [] + 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 + + [] + 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 + + [] + let (|Coerce|_|) (e : Expr) = + match e with + | E(CombTerm(CoerceOp(t), [a])) -> Some(a,t) + | _ -> None + + [] + let (|NewArray|_|) (e : Expr) = + match e with + | E(CombTerm(NewArrayOp(t), args)) -> Some(t, args) + | _ -> None + + + let rec private (|NLambdas|_|) n (e:Expr) = + match e with + | _ when n <= 0 -> Some([], e) + | Lambda(v, NLambdas ((-) n 1) (vs, b)) -> Some(v::vs, b) + | _ -> None + + [] + let (|NewDelegate|_|) input = + match input with + | CombTerm(NewDelegateOp(ty, nargs), [e]) -> + 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 + + + [] + let (|Quote|_|) (E x) = match x with CombTerm(QuoteOp _, [a]) -> Some (a) | _ -> None + + [] + let (|QuoteRaw|_|) (E x) = match x with CombTerm(QuoteOp false, [a]) -> Some (a) | _ -> None + + [] + let (|QuoteTyped|_|) (E x) = match x with CombTerm(QuoteOp true, [a]) -> Some (a) | _ -> None + + [] + let (|DefaultValue|_|) input = match input with E(CombTerm(DefaultValueOp(ty), [])) -> Some(ty) | _ -> None + // static member Quote(e : Expr) = + // Expr(CombTerm(QuoteOp true, [e])) + // static member QuoteRaw(e : Expr) = + // Expr(CombTerm(QuoteOp false, [e])) + // static member QuoteTyped(e : Expr) = + // Expr(CombTerm(QuoteOp false, [e])) + // static member Sequential(l : Expr, r : Expr) = + // Expr(CombTerm(SequentialOp, [l;r])) + // static member TryWith(body : Expr, filterVar : Var, filterBody : Expr, catchVar : Var, catchExpr : Expr) = + // Expr(CombTerm(TryWithOp, [body; Expr.Lambda(filterVar, filterBody); Expr.Lambda(catchVar, catchExpr)])) + // static member TryFinally(body : Expr, compensation : Expr) = + // Expr(CombTerm(TryFinallyOp, [body; compensation])) + // static member ForIntegerRangeLoop(i : Var, s : Expr, e : Expr, body : Expr) = + // Expr(CombTerm(ForIntegerRangeLoopOp, [s;e;Expr.Lambda(i, body)])) + // static member WhileLoop(cond : Expr, body : Expr) = + // Expr(CombTerm(WhileLoopOp, [cond; body])) + // static member VarSet(v : Var, value : Expr) = + // Expr (CombTerm(VarSetOp,[Expr.Var v; value])) + // static member AddressSet(addr : Expr, value : Expr) = + // Expr (CombTerm(AddressSetOp,[addr; value])) + // static member TypeTest(source : Expr, target : System.Type) = + // Expr (CombTerm(TypeTestOp target,[source])) + // static member DefaultValue(t : System.Type) = + // Expr (CombTerm(DefaultValueOp t, [])) + // static member WithValue(value: obj, expressionType: System.Type, definition: Expr) = + // Expr (CombTerm(WithValueOp(value, expressionType), [definition])) \ No newline at end of file diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 5fa7c86e32..7cce1ec373 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -457,6 +457,10 @@ export class NTypeInfo { this.declaration = decl || null; } + public MakeArrayType(): NTypeInfo { + return array(this); + } + public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genericMap) { diff --git a/tests/Main/DateTimeOffsetTests.fs b/tests/Main/DateTimeOffsetTests.fs index f6c661bdc5..cf69f6be4a 100644 --- a/tests/Main/DateTimeOffsetTests.fs +++ b/tests/Main/DateTimeOffsetTests.fs @@ -48,11 +48,11 @@ let tests = // dof.Offset |> equal (TimeSpan.FromHours(3.)) // dof.ToString("HH:mm") |> equal "17:30" - testCase "DateTimeOffset from Year 1 to 99 works" <| fun () -> - let date = DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.Zero) - date.Year |> equal 1 - let date = DateTimeOffset(99, 1, 1, 0, 0, 0, TimeSpan.Zero) - date.Year |> equal 99 + // testCase "DateTimeOffset from Year 1 to 99 works" <| fun () -> + // let date = DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.Zero) + // date.Year |> equal 1 + // let date = DateTimeOffset(99, 1, 1, 0, 0, 0, TimeSpan.Zero) + // date.Year |> equal 99 // TODO: These two tests give different values for .NET and JS because DateTimeOffset // becomes as a plain JS Date object, so I'm just checking the fields get translated diff --git a/tests/Main/DateTimeTests.fs b/tests/Main/DateTimeTests.fs index 1b5e018653..e12c4f11fd 100644 --- a/tests/Main/DateTimeTests.fs +++ b/tests/Main/DateTimeTests.fs @@ -62,11 +62,11 @@ let tests = let date = DateTime(99, 1, 1) date.Year |> equal 99 - testCase "DateTime UTC from Year 1 to 99 works" <| fun () -> - let date = DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc) - date.Year |> equal 1 - let date = DateTime(99, 1, 1, 0, 0, 0, DateTimeKind.Utc) - date.Year |> equal 99 + // testCase "DateTime UTC from Year 1 to 99 works" <| fun () -> + // let date = DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc) + // date.Year |> equal 1 + // let date = DateTime(99, 1, 1, 0, 0, 0, DateTimeKind.Utc) + // date.Year |> equal 99 // TODO: These two tests give different values for .NET and JS because DateTime // becomes as a plain JS Date object, so I'm just checking the fields get translated diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 876733ccde..a34524bb7b 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -167,8 +167,8 @@ let tests = | _ -> failwith "bad record" - testCase "CustomAttributes" <| fun () -> + // testCase "CustomAttributes" <| fun () -> - let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) - failwithf "prop: %s" (prop.ToPrettyString()) + // let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) + // failwithf "prop: %s" (prop.ToPrettyString()) ] \ No newline at end of file diff --git a/tests/Main/Fable.Tests.fsproj b/tests/Main/Fable.Tests.fsproj index 52301d0ca9..09b507712b 100644 --- a/tests/Main/Fable.Tests.fsproj +++ b/tests/Main/Fable.Tests.fsproj @@ -63,6 +63,7 @@ + diff --git a/tests/Main/Main.fs b/tests/Main/Main.fs index ea36e5bce6..cf0f15243a 100644 --- a/tests/Main/Main.fs +++ b/tests/Main/Main.fs @@ -44,6 +44,7 @@ let allTests = TypeTests.tests UnionTypes.tests Uri.tests + Expr.tests |] #if FABLE_COMPILER From 57445e9523936510dc3e5722f1092d13ab33acae Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 9 Apr 2019 17:01:58 +0200 Subject: [PATCH 12/38] added missing get_FieldType function --- src/Fable.Transforms/Replacements.fs | 1 + src/fable-library/Quotations.fs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index cb260ca3a6..41aa2ba413 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2904,6 +2904,7 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | Some c, "get_ReturnParameter" -> Helper.InstanceCall(c, "get_ReturnParameter", t, []) |> Some | Some c, "GetFields" -> Helper.InstanceCall(c, "GetFields", t, []) |> Some | Some c, "get_PropertyType" -> Helper.InstanceCall(c, "get_PropertyType", t, []) |> Some + | Some c, "get_FieldType" -> Helper.InstanceCall(c, "get_FieldType", t, []) |> Some | Some c, "get_ParameterType" -> Helper.InstanceCall(c, "get_ParameterType", t, []) |> Some | Some c, "get_GetMethod" -> Helper.InstanceCall(c, "get_GetMethod", t, []) |> Some | Some c, "get_SetMethod" -> Helper.InstanceCall(c, "get_SetMethod", t, []) |> Some diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 5acf8733d4..095a307596 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -62,6 +62,29 @@ module Helpers = 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 + open Fable.Import.JS + type ByteStream(bytes:Uint8Array, 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 = uint8 bytes.[pos] + pos <- pos + 1 + res + + member b.ReadBytes n = + if pos + n > lim then failwith "ByteStream.ReadBytes: end of stream" + let res = bytes.slice(float pos, float (pos + n)) //.[pos..pos+n-1] + pos <- pos + n + res + + member b.ReadUtf8BytesAsString n = + let res = System.Text.Encoding.UTF8.GetString(unbox bytes, pos, n) + pos <- pos + n + res + type ExprConstInfo = | AppOp | LetOp From e398346572d67ec2a5f6eaf6629322552793cf41 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Thu, 11 Apr 2019 11:19:42 +0200 Subject: [PATCH 13/38] Module reflection --- src/Fable.Transforms/AST/AST.Fable.fs | 3 +- src/Fable.Transforms/FSharp2Fable.Util.fs | 14 +- src/Fable.Transforms/FSharp2Fable.fs | 33 +- src/Fable.Transforms/Fable2Babel.fs | 64 +- src/Fable.Transforms/FableTransforms.fs | 6 +- src/Fable.Transforms/Replacements.fs | 55 +- src/fable-library/ExprUtils.fs | 2 +- src/fable-library/Quotations.fs | 2309 +++++++++++++++++---- src/fable-library/Reflection.ts | 101 +- tests/Main/ExprTests.fs | 6 +- 10 files changed, 2098 insertions(+), 495 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 7ba1507fc0..034cdcd2aa 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -127,7 +127,8 @@ type Declaration = | ActionDeclaration of Expr | ValueDeclaration of Expr * ValueDeclarationInfo | AttachedMemberDeclaration of args: Ident list * body: Expr * AttachedMemberDeclarationInfo - | ConstructorDeclaration of ConstructorKind * SourceLocation option + | ConstructorDeclaration of declaringName : Option * ConstructorKind * SourceLocation option + | ModuleDeclaration of declaringName : Option * name : string * ent : FSharpEntity * mems : MemberInfo[] type File(sourcePath, decls, ?usedVarNames, ?inlineDependencies) = member __.SourcePath: string = sourcePath diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 1703486776..e6d08c56e3 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -22,7 +22,7 @@ type Context = InlinePath: (string * (SourceLocation option)) list CaptureBaseConsCall: (FSharpEntity * (Fable.Expr * Fable.Expr -> unit)) option } - static member Create(enclosingEntity) = + static member Create(enclosingEntity:Option) = { Scope = [] ScopeInlineValues = [] GenericArgs = Map.empty @@ -99,6 +99,18 @@ module Helpers = (getEntityMangledName com true ent, Naming.NoMemberPart) ||> Naming.sanitizeIdent (fun _ -> false) + let getModuleReflectionName (com: ICompiler) (ent : FSharpEntity) = + if ent.IsFSharpModule then + let name = + (getEntityMangledName com false ent, Naming.NoMemberPart) + ||> Naming.sanitizeIdent (fun _ -> false) + if name = "" then + None + else + Some ("Module$" + name) + else + None + let isUnit (typ: FSharpType) = let typ = nonAbbreviatedType typ if typ.HasTypeDefinition diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 8336ceda1f..f3a5212815 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -963,7 +963,8 @@ let private transformImplicitConstructor com (ctx: Context) // TODO!!! When adding a ConstructorDeclaration check if there are // name clashes for interface/abstract members let r = getEntityLocation ent |> makeRange - [Fable.ConstructorDeclaration(Fable.ClassImplicitConstructor info, Some r)] + let decl = ctx.EnclosingEntity |> Option.bind (Helpers.getModuleReflectionName com) + [Fable.ConstructorDeclaration(decl, Fable.ClassImplicitConstructor info, Some r)] /// When using `importMember`, uses the member display name as selector let private importExprSelector (memb: FSharpMemberOrFunctionOrValue) selector = @@ -1153,7 +1154,8 @@ let private transformDeclarations (com: FableCompiler) ctx rootEnt rootDecls = EntityName = entityName IsPublic = isPublicEntity ent } let r = getEntityLocation ent |> makeRange - [Fable.ConstructorDeclaration(Fable.UnionConstructor info, Some r)] + let decl = ctx.EnclosingEntity |> Option.bind (Helpers.getModuleReflectionName com) + [Fable.ConstructorDeclaration(decl, Fable.UnionConstructor info, Some r)] | _ when ent.IsFSharpRecord || ent.IsFSharpExceptionDeclaration || ((ent.IsClass || ent.IsValueType) && not ent.IsMeasure && not (hasImplicitConstructor ent)) -> @@ -1168,10 +1170,22 @@ let private transformDeclarations (com: FableCompiler) ctx rootEnt rootDecls = Members = props } let r = getEntityLocation ent |> makeRange - - [Fable.ConstructorDeclaration(Fable.CompilerGeneratedConstructor info, Some r)] + let decl = ctx.EnclosingEntity |> Option.bind (Helpers.getModuleReflectionName com) + [Fable.ConstructorDeclaration(decl, Fable.CompilerGeneratedConstructor info, Some r)] + | _ when ent.IsFSharpModule -> + let declaring = ctx.EnclosingEntity |> Option.bind (Helpers.getModuleReflectionName com) + let name = Helpers.getModuleReflectionName com ent |> Option.get + let decl = + try Fable.ModuleDeclaration (declaring, name, ent, transformMemberReflectionInfos com ctx ent) |> Some + with e -> e |> sprintf "%A" |> addWarning com [] None; None + match decl with + | Some decl -> + decl :: transformDeclarationsInner com { ctx with EnclosingEntity = Some ent } sub + | None -> + transformDeclarationsInner com { ctx with EnclosingEntity = Some ent } sub | _ -> transformDeclarationsInner com { ctx with EnclosingEntity = Some ent } sub + | FSharpImplementationFileDeclaration.MemberOrFunctionOrValue(meth, args, body) -> transformMemberDecl com ctx meth args body | FSharpImplementationFileDeclaration.InitAction fe -> @@ -1282,8 +1296,15 @@ let transformFile (com: ICompiler) (implFiles: IDictionary + let ctx = Context.Create(None) + transformDeclarations fcom ctx None [FSharpImplementationFileDeclaration.Entity(ent, rootDecls)] + | _ -> + let ctx = Context.Create(rootEnt) + transformDeclarations fcom ctx rootEnt rootDecls Fable.File(com.CurrentFile, rootDecls, set fcom.UsedVarNames, set fcom.InlineDependencies) with | ex -> exn (sprintf "%s (%s)" ex.Message com.CurrentFile, ex) |> raise diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 5e8f1c6684..5cba9e089e 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -460,7 +460,7 @@ module Util = | Fable.MemberInfoKind.Field(name, typ, isStatic) -> newField self isStatic typ name attributes ) - and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = + and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) declaringName (mems : Fable.MemberInfo[]) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression @@ -471,7 +471,15 @@ module Util = let gen = Identifier "gen" let nMembers = transformMemberReflectionInfosNew com ctx r self gen ent mems let fields = FunctionExpression([|toPattern self; toPattern gen|], BlockStatement [| ReturnStatement(ArrayExpression nMembers) :> Statement |]) :> Expression - [|fullnameExpr; genParamNames; upcast ArrayExpression generics; fields|] + let decl = + match declaringName with + | Some decl -> + let reflName = decl + "$" + Naming.reflectionSuffix + CallExpression(Identifier reflName, [||]) :> Expression + | None -> + NullLiteral() :> Expression + + [|fullnameExpr; genParamNames; upcast ArrayExpression generics; fields; decl|] |> coreLibCall com ctx None "Reflection" "ntype" // let members = transformMemberReflectionInfos com ctx r ent mems generics // //let fields = ArrowFunctionExpression([||], ArrayExpression members :> Expression |> U2.Case2) :> Expression @@ -589,7 +597,7 @@ module Util = { Fable.Kind = Fable.MemberInfoKind.UnionCaseConstructor(0, "Ok", [|"value", ok|], "Ok", "_Option.Result"); Fable.Attributes = [||] } { Fable.Kind = Fable.MemberInfoKind.UnionCaseConstructor(1, "Error", [|"value", err|], "Error", "_Option.Result"); Fable.Attributes = [||] } |] - transformRecordReflectionInfo com ctx r ent resultCases [| + transformRecordReflectionInfo com ctx r ent None resultCases [| transformTypeInfo com ctx r [||] genMap ok transformTypeInfo com ctx r [||] genMap err |] @@ -602,9 +610,9 @@ module Util = ) let gen = List.map (transformTypeInfo com ctx r [||] genMap) gen - List.toArray gen |> transformRecordReflectionInfo com ctx r ent cases + List.toArray gen |> transformRecordReflectionInfo com ctx r ent None cases | Replacements.FSharpReference gen -> - transformRecordReflectionInfo com ctx r ent mems [|transformTypeInfo com ctx r [||] genMap gen|] + transformRecordReflectionInfo com ctx r ent None mems [|transformTypeInfo com ctx r [||] genMap gen|] | _ -> let generics = generics |> List.map (transformTypeInfo com ctx r [||] genMap) |> List.toArray /// Check if the entity is actually declared in JS code @@ -617,8 +625,8 @@ module Util = let reflectionMethodExpr = FSharp2Fable.Util.entityRefWithSuffix com ent Naming.reflectionSuffix CallExpression(com.TransformAsExpr(ctx, reflectionMethodExpr), generics) :> Expression - let transformReflectionInfo com ctx r (ent: FSharpEntity) (mems : Fable.MemberInfo[]) generics = - transformRecordReflectionInfo com ctx r ent mems generics + let transformReflectionInfo com ctx r (ent: FSharpEntity) declaringName (mems : Fable.MemberInfo[]) generics = + transformRecordReflectionInfo com ctx r ent declaringName mems generics // if ent.IsFSharpRecord then // transformRecordReflectionInfo com ctx r ent mems generics // elif ent.IsFSharpUnion then @@ -996,6 +1004,14 @@ module Util = | Replacements.FSharpReference _ -> fail "result/choice/reference" | Fable.DeclaredType (ent, genArgs) -> match ent.TryFullName with + | Some "System.Type" -> coreLibCall com ctx None "Reflection" "isType" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.MemberInfo" -> coreLibCall com ctx None "Reflection" "isMemberInfo" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.MethodBase" -> coreLibCall com ctx None "Reflection" "isMethodBase" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.MethodInfo" -> coreLibCall com ctx None "Reflection" "isMethodInfo" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.ConstructorInfo" -> coreLibCall com ctx None "Reflection" "isConstructorInfo" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.FieldInfo" -> coreLibCall com ctx None "Reflection" "isFieldInfo" [|com.TransformAsExpr(ctx, expr)|] + | Some "System.Reflection.PropertyInfo" -> coreLibCall com ctx None "Reflection" "isPropertyInfo" [|com.TransformAsExpr(ctx, expr)|] + | Some "Microsoft.FSharp.Reflection.UnionCaseInfo" -> coreLibCall com ctx None "Reflection" "isUnionCaseInfo" [|com.TransformAsExpr(ctx, expr)|] | Some Types.idisposable -> match expr.Type with // In F# AST this is coerced to obj, but the cast should have been removed @@ -1520,7 +1536,7 @@ module Util = ExportNamedDeclaration(decl) :> ModuleDeclaration |> U2.Case2 - let declareType com ctx r isPublic (mems : Fable.MemberInfo[]) (ent: FSharpEntity) name consArgs consBody baseExpr: U2 list = + let declareType com ctx r isPublic declaringName (mems : Fable.MemberInfo[]) (ent: FSharpEntity) name consArgs consBody baseExpr: U2 list = let displayName = ent.TryGetFullDisplayName() |> Option.map (Naming.unsafeReplaceIdentForbiddenChars '_') @@ -1534,7 +1550,7 @@ module Util = |> declareModuleMember isPublic name false let reflectionDeclaration = let genArgs = Array.init ent.GenericParameters.Count (fun _ -> makeIdentUnique com "gen" |> ident) - let body = transformReflectionInfo com ctx r ent mems (Array.map (fun x -> x :> _) genArgs) + let body = transformReflectionInfo com ctx r ent declaringName mems (Array.map (fun x -> x :> _) genArgs) makeFunctionExpression None (Array.map (fun x -> U2.Case2(upcast x)) genArgs) (U2.Case2 body) |> declareModuleMember isPublic (Naming.appendSuffix name Naming.reflectionSuffix) false [typeDeclaration; reflectionDeclaration] @@ -1603,7 +1619,7 @@ module Util = |> ExpressionStatement :> Statement |> U2<_,ModuleDeclaration>.Case1 |> List.singleton - let transformUnionConstructor (com: IBabelCompiler) ctx r (info: Fable.UnionConstructorInfo) = + let transformUnionConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.UnionConstructorInfo) = let baseRef = coreValue com ctx "Types" "Union" let args = [|Identifier "tag" |> toPattern @@ -1612,9 +1628,9 @@ module Util = let body = [Identifier "tag" :> Expression; Identifier "name" :> _; SpreadElement(Identifier "fields") :> _] |> callFunctionWithThisContext None baseRef thisExpr |> ExpressionStatement - declareType com ctx r info.IsPublic info.Members info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) + declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) - let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (info: Fable.CompilerGeneratedConstructorInfo) = + let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.CompilerGeneratedConstructorInfo) = let args = [| for i = 1 to info.Entity.FSharpFields.Count do yield Identifier("arg" + string i) |] @@ -1636,9 +1652,9 @@ module Util = then coreValue com ctx "Types" "Record" |> Some else None let args = [|for arg in args do yield arg |> toPattern|] - declareType com ctx r info.IsPublic info.Members info.Entity info.EntityName args (BlockStatement setters) baseExpr + declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args (BlockStatement setters) baseExpr - let transformImplicitConstructor (com: IBabelCompiler) ctx r (info: Fable.ClassImplicitConstructorInfo) = + let transformImplicitConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.ClassImplicitConstructorInfo) = let boundThis = Some("this", info.BoundConstructorThis) let consIdent = Identifier info.EntityName :> Expression let args, body = getMemberArgsAndBody com ctx None boundThis info.Arguments info.HasSpread info.Body @@ -1668,7 +1684,7 @@ module Util = | None when info.Entity.IsValueType -> coreValue com ctx "Types" "Record" |> Some | None -> None [ - yield! declareType com ctx r info.IsEntityPublic info.Members info.Entity info.EntityName args body baseExpr + yield! declareType com ctx r info.IsEntityPublic declaringName info.Members info.Entity info.EntityName args body baseExpr yield declareModuleMember info.IsConstructorPublic info.Name false exposedCons ] @@ -1697,15 +1713,15 @@ module Util = [declareModuleMember info.IsPublic info.Name info.IsMutable value] |> List.append transformed |> transformDeclarations com ctx restDecls - | Fable.ConstructorDeclaration(kind, r) -> + | Fable.ConstructorDeclaration(declaringName, kind, r) -> let consDecls = match kind with | Fable.ClassImplicitConstructor info -> - transformImplicitConstructor com ctx r info + transformImplicitConstructor com ctx r declaringName info | Fable.UnionConstructor info -> - transformUnionConstructor com ctx r info + transformUnionConstructor com ctx r declaringName info | Fable.CompilerGeneratedConstructor info -> - transformCompilerGeneratedConstructor com ctx r info + transformCompilerGeneratedConstructor com ctx r declaringName info consDecls |> List.append transformed |> transformDeclarations com ctx restDecls @@ -1728,7 +1744,15 @@ module Util = | _ -> transformOverrideMethod com ctx info args body, restDecls List.append transformed newDecls |> transformDeclarations com ctx restDecls - + | Fable.ModuleDeclaration(declaringName, name, ent, mems) -> + //sprintf "module %s" name |> addWarning com [] None + //transformed |> transformDeclarations com ctx restDecls + let reflectionDeclaration = + let body = transformRecordReflectionInfo com ctx None ent declaringName mems [||] + makeFunctionExpression None [||] (U2.Case2 body) + |> declareModuleMember true (Naming.appendSuffix name Naming.reflectionSuffix) false + List.append transformed [reflectionDeclaration] + |> transformDeclarations com ctx restDecls let transformImports (imports: Import seq): U2 list = imports |> Seq.map (fun import -> let specifier = diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 286a7c7cd7..1d3ee98056 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -553,11 +553,13 @@ let optimizeExpr (com: ICompiler) e = List.fold (fun e f -> f com e) e optimizations let rec optimizeDeclaration (com: ICompiler) = function + | ModuleDeclaration(decl, name, ent, mems) -> + ModuleDeclaration(decl, name, ent, mems) | ActionDeclaration expr -> ActionDeclaration(optimizeExpr com expr) | ValueDeclaration(value, info) -> ValueDeclaration(optimizeExpr com value, info) - | ConstructorDeclaration(kind, r) -> + | ConstructorDeclaration(decl, kind, r) -> let kind = match kind with | ClassImplicitConstructor info -> @@ -573,7 +575,7 @@ let rec optimizeDeclaration (com: ICompiler) = function info.Arguments, info.Body ClassImplicitConstructor { info with Arguments = args; Body = body } | kind -> kind - ConstructorDeclaration(kind, r) + ConstructorDeclaration(decl, kind, r) | AttachedMemberDeclaration(args, body, info) -> AttachedMemberDeclaration(args, optimizeExpr com body, info) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 41aa2ba413..f617f3f179 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2475,13 +2475,20 @@ let exprs (name : string) (_: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo match i.CompiledName, thisArg, args with //| meth, Some x,_ -> Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, ?loc=r) |> Some | "Value", None, _ -> + let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None match args with - | [a] -> - let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None - Helper.CoreCall("Quotations", name, t, [a; makeTypeInfo None a.Type], i.SignatureArgTypes, ?loc=r) |> Some - | _ -> - let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None - Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + | [a] -> Helper.CoreCall("Quotations", name, t, [a; makeTypeInfo None a.Type], i.SignatureArgTypes, ?loc=r) |> Some + | _ -> Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + | "ValueWithName", None, _ -> + let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None + match args with + | [a;n] -> Helper.CoreCall("Quotations", name, t, [a; makeTypeInfo None a.Type; n], i.SignatureArgTypes, ?loc=r) |> Some + | _ -> Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + | "WithValue", None, _ -> + let _,name = getMangledNames { i with OverloadSuffix = lazy ("") } None + match args with + | [a;n] -> Helper.CoreCall("Quotations", name, t, [a; makeTypeInfo None a.Type; n], i.SignatureArgTypes, ?loc=r) |> Some + | _ -> Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some | meth, Some x,_ -> @@ -2490,6 +2497,12 @@ let exprs (name : string) (_: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo | meth, None,_ -> + //match t with + // | Fable.Expr _ -> + // let hash = match i.Original with | Some o -> lazy (OverloadSuffix.getExtensionHash o) | _ -> lazy "" + // let _,name = getMangledNames { i with OverloadSuffix = hash } None + // Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some + // | _ -> let _,name = getMangledNames i None Helper.CoreCall("Quotations", name, t, args, i.SignatureArgTypes, ?loc=r) |> Some @@ -2607,30 +2620,42 @@ let types (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio match thisArg, i.CompiledName with + | Some this, "get_IsGenericParameter" -> callTypeInfoMethod this "get_IsGenericParameter" t [] |> Some | Some this, "get_FullName" -> callTypeInfoMethod this "get_FullName" t [] |> Some | Some this, "get_Namespace" -> callTypeInfoMethod this "get_Namespace" t [] |> Some | Some this, "get_IsArray" -> callTypeInfoMethod this "get_IsArray" t [] |> Some | Some this, "get_IsGenericType" -> callTypeInfoMethod this "get_IsGenericType" t [] |> Some | Some this, "get_IsGenericTypeDefinition" -> callTypeInfoMethod this "get_IsGenericTypeDefinition" t [] |> Some | Some this, "get_GenericTypeArguments" -> callTypeInfoMethod this "get_GenericTypeArguments" t [] |> Some + | Some this, "get_DeclaringType" -> callTypeInfoMethod this "get_DeclaringType" t [] |> Some | Some this, "GetElementType" -> callTypeInfoMethod this "GetElementType" t [] |> Some | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some | Some this, "GetGenericTypeDefinition" -> callTypeInfoMethod this "GetGenericTypeDefinition" t [] |> Some | Some this, "MakeArrayType" -> callTypeInfoMethod this "MakeArrayType" t [] |> Some | Some this, "GetTypeInfo" -> Some this + | Some this, "GetProperties" -> callTypeInfoMethod this "GetProperties" t args |> Some | Some this, "GetMethods" -> callTypeInfoMethod this "GetMethods" t [] |> Some + | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some + | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + | Some this, "GetConstructors" -> callTypeInfoMethod this "GetConstructors" t args |> Some + + | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some | Some this, "GetMethod" -> match args with - | [a] when a.Type = Fable.String -> callTypeInfoMethod this "GetMethod" t [a] |> Some - | a::b::_ when a.Type = Fable.String && b.Type = Fable.Array (Fable.MetaType) -> callTypeInfoMethod this "GetMethod" t [a; b] |> Some + | [a] when a.Type = Fable.String -> callTypeInfoMethod this "GetMethod" t [a] |> Some + | [a;b] when a.Type = Fable.String -> callTypeInfoMethod this "GetMethod" t [a; b] |> Some | a::_::_::b::_ when a.Type = Fable.String && b.Type = Fable.Array (Fable.MetaType) -> callTypeInfoMethod this "GetMethod" t [a; b] |> Some | _ -> None - | Some this, "GetMembers" -> callTypeInfoMethod this "GetMembers" t args |> Some - | Some this, "GetFields" -> callTypeInfoMethod this "GetFields" t args |> Some + | Some this, "GetField" -> callTypeInfoMethod this "GetField" t [List.head args] |> Some + + | Some this, "GetConstructor" -> + match args with + | [] -> callTypeInfoMethod this "GetConstructor" t [] |> Some + | [ts] -> callTypeInfoMethod this "GetConstructor" t [ts] |> Some + | _ -> None | Some this, "MakeGenericType" -> callTypeInfoMethod this "MakeGenericType" t args |> Some - | Some this, "GetProperty" -> callTypeInfoMethod this "GetProperty" t [List.head args] |> Some | _ -> None // match thisArg with @@ -2888,11 +2913,12 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | "FSharp.Reflection.UnionCaseInfo" | "System.Reflection.MethodInfo" -> match thisArg, info.CompiledName with - + | Some this, "GetGenericArguments" -> callTypeInfoMethod this "GetGenericArguments" t [] |> Some | Some this, "GetGenericMethodDefinition" -> callTypeInfoMethod this "GetGenericMethodDefinition" t [] |> Some | Some this, "get_IsGenericMethod" -> callTypeInfoMethod this "get_IsGenericMethod" t [] |> Some | Some this, "get_IsGenericMethodDefinition" -> callTypeInfoMethod this "get_IsGenericMethodDefinition" t [] |> Some | Some this, "MakeGenericMethod" -> callTypeInfoMethod this "MakeGenericMethod" t args |> Some + | Some c, "GetIndexParameters" -> Helper.InstanceCall(c, "GetIndexParameters", t, args) |> Some | Some c, "Invoke" -> Helper.InstanceCall(c, "Invoke", t, args) |> Some | Some c, "get_Name" -> Helper.InstanceCall(c, "get_Name", t, []) |> Some | Some c, "get_Tag" -> Helper.InstanceCall(c, "get_Tag", t, []) |> Some @@ -2908,7 +2934,10 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr | Some c, "get_ParameterType" -> Helper.InstanceCall(c, "get_ParameterType", t, []) |> Some | Some c, "get_GetMethod" -> Helper.InstanceCall(c, "get_GetMethod", t, []) |> Some | Some c, "get_SetMethod" -> Helper.InstanceCall(c, "get_SetMethod", t, []) |> Some - + | Some c, "GetGetMethod" -> Helper.InstanceCall(c, "get_GetMethod", t, [])|> Some + | Some c, "GetSetMethod" -> Helper.InstanceCall(c, "get_SetMethod", t, [])|> Some + | Some c, "get_CanRead" -> Helper.InstanceCall(c, "get_CanRead", t, [])|> Some + | Some c, "get_CanWrite" -> Helper.InstanceCall(c, "get_CanWrite", t, [])|> Some | Some c, "SetValue" -> match args with | [a;v] -> Helper.InstanceCall(c, "SetValue", t, [a; v]) |> Some diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index 12f6354744..afac3db6dc 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -1,6 +1,6 @@ module ExprUtils -open Quotations +open Microsoft.FSharp.Quotations let deserialize (str : string) = let v = Var("a", typeof) diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 095a307596..dc4e210e04 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -1,42 +1,30 @@ -module Quotations - +// 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 Fable.Core -open FSharp.Reflection - -[] -type Var(name : string, typ : System.Type, ?isMutable : bool) = - let isMutable = match isMutable with | None -> false | Some m -> m - static let mutable currentStamp = 0 - let stamp = - let v = currentStamp - currentStamp <- currentStamp + 1 - v - - member internal x.Stamp = stamp - member x.Name = name - member x.Type = typ - member x.IsMutable = isMutable - override x.GetHashCode() = stamp - override x.Equals o = - match o with - | :? Var as o -> o.Stamp = stamp - | _ -> false +#nowarn "52" // The value has been copied to ensure the original is not mutated by this operation - override x.ToString() = name - - interface System.IComparable with - member x.CompareTo(o : obj) = - match o with - | :? Var as o -> - let c = compare name o.Name - if c <> 0 then c - else - let c = compare typ.Name o.Type.Name - if c <> 0 then c - else compare stamp o.Stamp - | _ -> - failwith "Var can only be compared to Var" +#if FX_RESHAPED_REFLECTION +open PrimReflectionAdapters +open ReflectionAdapters +#endif +//-------------------------------------------------------------------------- +// RAW quotations - basic data types +//-------------------------------------------------------------------------- module Helpers = let qOneOrMoreRLinear q inp = @@ -62,274 +50,200 @@ module Helpers = 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 - open Fable.Import.JS - type ByteStream(bytes:Uint8Array, initial:int, len:int) = - let mutable pos = initial - let lim = initial + len + let isDelegateType (typ:Type) = + typ.FullName.StartsWith "System.Func" - member b.ReadByte() = - if pos >= lim then failwith "end of stream" - let res = uint8 bytes.[pos] - pos <- pos + 1 - res + let getDelegateNargs (ty:Type) = + if ty.FullName.StartsWith "System.Func`" then + let ngen = ty.FullName.Substring(12, ty.FullName.Length - 12) |> int + ngen - 1 + else + 0 - member b.ReadBytes n = - if pos + n > lim then failwith "ByteStream.ReadBytes: end of stream" - let res = bytes.slice(float pos, float (pos + n)) //.[pos..pos+n-1] - pos <- pos + n - res - member b.ReadUtf8BytesAsString n = - let res = System.Text.Encoding.UTF8.GetString(unbox bytes, pos, n) - pos <- pos + n - res + 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 + + +[] +[] +[] +type Var(name: string, typ:Type, ?isMutable: bool) = + inherit obj() + + static let getStamp = + let mutable lastStamp = -1L // first value retrieved will be 0 + fun () -> + let s = lastStamp + lastStamp <- s + -1L + s + + 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 + let ok = globals.ContainsKey((name, typ)) //.TryGetValue((name, typ), &res) + if ok then globals.[(name, typ)] else + let res = 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 + let c = compare v.Type.FullName v2.Type.FullName + if c <> 0 then c else + compare v.Stamp v2.Stamp + | _ -> 0 + +/// Represents specifications of a subset of F# expressions +[] +type Tree = + | CombTerm of ExprConstInfo * Expr list + | VarTerm of Var + | LambdaTerm of Var * Expr + | HoleTerm of Type * int -type ExprConstInfo = +and + [] + ExprConstInfo = | AppOp - | LetOp - | LetRecOp - | LetRecCombOp - | ValueOp of obj * System.Type * Option | IfThenElseOp - | NewRecordOp of System.Type - | NewUnionCaseOp of UnionCaseInfo - | UnionCaseTestOp of UnionCaseInfo - | NewTupleOp of System.Type - | TupleGetOp of System.Type * int - | InstancePropGetOp of System.Reflection.PropertyInfo - | StaticPropGetOp of System.Reflection.PropertyInfo - | InstancePropSetOp of System.Reflection.PropertyInfo - | StaticPropSetOp of System.Reflection.PropertyInfo - | InstanceFieldGetOp of System.Reflection.FieldInfo - | StaticFieldGetOp of System.Reflection.FieldInfo - | InstanceFieldSetOp of System.Reflection.FieldInfo - | StaticFieldSetOp of System.Reflection.FieldInfo - | NewObjectOp of System.Reflection.ConstructorInfo - | InstanceMethodCallOp of System.Reflection.MethodInfo - | StaticMethodCallOp of System.Reflection.MethodInfo - | CoerceOp of System.Type - | NewArrayOp of System.Type - | NewDelegateOp of System.Type * int + | 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 System.Type + | TypeTestOp of Type | TryWithOp | TryFinallyOp | ForIntegerRangeLoopOp | WhileLoopOp - | WithValueOp of obj * System.Type - | DefaultValueOp of System.Type + // Arbitrary spliced values - not serialized + | ValueOp of obj * Type * string option + | WithValueOp of obj * Type + | DefaultValueOp of Type + +and [] + 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 -and Tree = - | CombTerm of ExprConstInfo * Expr list - | VarTerm of Var - | LambdaTerm of Var * Expr - - -and [] Expr(tree : Tree) = - static let (|E|) (e : Expr) = E (e.Tree) - static let (|L0|) (l : list<_>) = match l with [] -> L0 | _ -> failwith "bad list" - static let (|L1|) (l : list<_>) = match l with [a] -> L1(a) | _ -> failwith "bad list" - static let (|L2|) (l : list<_>) = match l with [a;b] -> L2(a,b) | _ -> failwith "bad list" - static let (|L3|) (l : list<_>) = match l with [a;b;c] -> L3(a,b,c) | _ -> failwith "bad list" - - static let (|Lambda|_|) (e : Expr) = match e.Tree with | LambdaTerm(v,b) -> Some (v,b) | _ -> None - static let (|IteratedLambda|_|) (e: Expr) = Helpers.qOneOrMoreRLinear (|Lambda|_|) e - static let rec typeOf (e : Expr) = - match e.Tree with - | VarTerm v -> v.Type - | LambdaTerm(v, b) -> FSharpType.MakeFunctionType(v.Type, typeOf b) - | CombTerm(t, c) -> - match t, c with - | AppOp, L2(f,_e) -> FSharpType.GetFunctionElements (typeOf f) |> snd - | LetOp, L3(_,_,b) -> typeOf b - | ValueOp(_,t,_), _ -> t - | IfThenElseOp, L3(_,i,_) -> typeOf i - - | LetRecOp, L1(IteratedLambda(_, E(CombTerm(LetRecCombOp, b2::_)))) -> typeOf b2 - | LetRecOp, _ -> failwith "internal error" - | LetRecCombOp, _ -> failwith "internal error" - - | NewRecordOp t, _ -> t - | NewUnionCaseOp c, _ -> c.DeclaringType - | UnionCaseTestOp _, _ -> typeof - | NewTupleOp t, _ -> t - | TupleGetOp(t, i), _ -> FSharpType.GetTupleElements(t).[i] - - | InstancePropGetOp p, _ -> p.PropertyType - | StaticPropGetOp p, _ -> p.PropertyType - | InstanceFieldGetOp p, _ -> p.FieldType - | StaticFieldGetOp p, _ -> p.FieldType - - | InstancePropSetOp _, _ -> typeof - | StaticPropSetOp _, _ -> typeof - | InstanceFieldSetOp _, _ -> typeof - | StaticFieldSetOp _, _ -> typeof - - | NewObjectOp c, _ -> c.DeclaringType - | InstanceMethodCallOp m, _ -> m.ReturnType - | StaticMethodCallOp m, _ -> m.ReturnType - - | CoerceOp t, _ -> t - | NewArrayOp t, _ -> t.MakeArrayType() - | NewDelegateOp(t,_), _ -> t - - | QuoteOp _, _ -> typeof - | SequentialOp, L2(_,r) -> r.Type - | AddressOfOp, L1(e) -> e.Type - | VarSetOp, _ -> typeof - | AddressSetOp, _ -> typeof - | TypeTestOp _, _ -> typeof - | TryWithOp, L3(t,_,_) -> t.Type - | TryFinallyOp, L2(t,_) -> t.Type - | ForIntegerRangeLoopOp, _ -> typeof - | WhileLoopOp, _ -> typeof - | WithValueOp(_,t), _ -> t - | DefaultValueOp t, _ -> t + override x.GetHashCode() = + x.Tree.GetHashCode() - member internal x.Tree = tree + // override x.ToString() = x.ToString(false) - member x.Type = typeOf x + // member x.ToString(full) = + // Microsoft.FSharp.Text.StructuredPrintfImpl.Display.layout_to_string Microsoft.FSharp.Text.StructuredPrintfImpl.FormatOptions.Default (x.GetLayout(full)) - override x.ToString() = - match tree with - | VarTerm v -> sprintf "Var %s" v.Name - | CombTerm(ValueOp(_,v,None),[]) -> sprintf "Value %A" v - | CombTerm(ValueOp(_,v,Some n),[])-> sprintf "ValueWithName(%A, %s)" v n - | CombTerm(LetOp, [e; E (LambdaTerm(v, b))]) -> sprintf "Let(%A, %s, %s)" v (string e) (string b) - | _ -> "bad expression" - [] - static member Value(o : obj, t : System.Type) = - Expr(CombTerm(ValueOp(o, t, None), [])) - - static member Var(v : Var) = - Expr(VarTerm v) - static member Lambda(v : Var, b : Expr) = - Expr(LambdaTerm(v, b)) - - static member Application(f : Expr, e : Expr) = - Expr(CombTerm(AppOp, [f; e])) - - static member Let(v : Var, e : Expr, b : Expr) = - Expr(CombTerm(LetOp, [e; Expr(LambdaTerm(v, b))])) - - - static member LetRecursive(bindings : list, body : Expr) = - match bindings with - | [] -> body - | [(v,e)] -> Expr.Let(v, e, body) - | many -> - let rec acc (all : list) (l : list) = - match l with - | (v,_e) :: rest -> - let rest = acc all rest - Expr.Lambda(v, rest) - | [] -> - Expr(CombTerm(LetRecCombOp, body :: (List.map snd all))) - Expr(CombTerm(LetRecOp, [acc many many])) - //| CombTerm(LetRecOp, [IteratedLambda(vs, E(CombTerm(LetRecCombOp, b2::bs)))]) -> Some(List.zip vs bs, b2) - - static member IfThenElse(c : Expr, i : Expr, e : Expr) = - Expr(CombTerm(IfThenElseOp, [c; i; e])) - - static member NewRecord(t : System.Type, args : list) = - Expr(CombTerm(NewRecordOp t, args)) - - static member NewUnionCase(t : UnionCaseInfo, args : list) = - Expr(CombTerm(NewUnionCaseOp t, args)) - - static member UnionCaseTest(t : UnionCaseInfo, arg : Expr) = - Expr(CombTerm(UnionCaseTestOp t, [arg])) - - static member NewTuple(t : System.Type, args : list) = - Expr(CombTerm(NewTupleOp t, args)) - - static member TupleGet(t : System.Type, n : int, self : Expr) = - Expr(CombTerm(TupleGetOp(t,n), [self])) - - static member PropertyGet(obj : Expr, property : System.Reflection.PropertyInfo, ?indexerArgs) = - let indexerArgs = defaultArg indexerArgs [] - Expr(CombTerm(InstancePropGetOp(property), obj :: indexerArgs)) - - static member PropertyGet(property : System.Reflection.PropertyInfo, ?indexerArgs) = - let indexerArgs = defaultArg indexerArgs [] - Expr(CombTerm(StaticPropGetOp(property), indexerArgs)) - - static member FieldGet(obj : Expr, field : System.Reflection.FieldInfo) = - Expr(CombTerm(InstanceFieldGetOp(field), [obj])) - - static member FieldGet(field : System.Reflection.FieldInfo) = - Expr(CombTerm(StaticFieldGetOp(field), [])) - - - static member PropertySet(obj : Expr, property : System.Reflection.PropertyInfo, value : Expr, ?indexerArgs) = - let indexerArgs = defaultArg indexerArgs [] - Expr(CombTerm(InstancePropSetOp(property), obj :: (indexerArgs @ [value]))) - - static member PropertySet(property : System.Reflection.PropertyInfo, value : Expr, ?indexerArgs) = - let indexerArgs = defaultArg indexerArgs [] - Expr(CombTerm(StaticPropSetOp(property), indexerArgs @ [value])) - - static member FieldSet(obj : Expr, field : System.Reflection.FieldInfo, value : Expr) = - Expr(CombTerm(InstanceFieldSetOp(field), [obj;value])) - - static member FieldSet(field : System.Reflection.FieldInfo, value : Expr) = - Expr(CombTerm(StaticFieldSetOp(field), [value])) - - static member Coerce(source : Expr, target : System.Type) = - Expr(CombTerm(CoerceOp(target), [source])) - static member NewArray(elementType : System.Type, elements : list) = - Expr(CombTerm(NewArrayOp(elementType), elements)) - - static member NewDelegate (delegateType : System.Type, parameters: Var list, body: Expr) = - Expr(CombTerm(NewDelegateOp(delegateType, parameters.Length), [Helpers.mkRLinear Expr.Lambda (parameters, body)])) - - static member Quote(e : Expr) = - Expr(CombTerm(QuoteOp true, [e])) - static member QuoteRaw(e : Expr) = - Expr(CombTerm(QuoteOp false, [e])) - static member QuoteTyped(e : Expr) = - Expr(CombTerm(QuoteOp false, [e])) - static member Sequential(l : Expr, r : Expr) = - Expr(CombTerm(SequentialOp, [l;r])) - static member TryWith(body : Expr, filterVar : Var, filterBody : Expr, catchVar : Var, catchExpr : Expr) = - Expr(CombTerm(TryWithOp, [body; Expr.Lambda(filterVar, filterBody); Expr.Lambda(catchVar, catchExpr)])) - static member TryFinally(body : Expr, compensation : Expr) = - Expr(CombTerm(TryFinallyOp, [body; compensation])) - static member ForIntegerRangeLoop(i : Var, s : Expr, e : Expr, body : Expr) = - Expr(CombTerm(ForIntegerRangeLoopOp, [s;e;Expr.Lambda(i, body)])) - static member WhileLoop(cond : Expr, body : Expr) = - Expr(CombTerm(WhileLoopOp, [cond; body])) - static member VarSet(v : Var, value : Expr) = - Expr (CombTerm(VarSetOp,[Expr.Var v; value])) - static member AddressSet(addr : Expr, value : Expr) = - Expr (CombTerm(AddressSetOp,[addr; value])) - static member TypeTest(source : Expr, target : System.Type) = - Expr (CombTerm(TypeTestOp target,[source])) - static member DefaultValue(t : System.Type) = - Expr (CombTerm(DefaultValueOp t, [])) - static member WithValue(value: obj, expressionType: System.Type, definition: Expr) = - Expr (CombTerm(WithValueOp(value, expressionType), [definition])) - - static member Call (methodInfo:System.Reflection.MethodInfo, arguments) = - Expr (CombTerm(StaticMethodCallOp(methodInfo), arguments)) - - static member Call (obj:Expr, methodInfo:System.Reflection.MethodInfo, arguments) = - Expr (CombTerm(InstanceMethodCallOp(methodInfo), obj::arguments)) - - static member NewObject (constructorInfo:System.Reflection.ConstructorInfo, arguments) = - Expr (CombTerm(NewObjectOp constructorInfo, arguments)) + +and [] + Expr<'T>(term:Tree, attribs) = + inherit Expr(term, attribs) + member x.Raw = (x :> Expr) [] module Patterns = - let E t = Expr(t) - let EA (t, attribs) = Expr(t) + /// 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 @@ -341,6 +255,7 @@ module Patterns = let funTyC = typeof<(obj -> obj)>.GetGenericTypeDefinition() + let exprTyC = typedefof> let voidTy = typeof let unitTy = typeof let removeVoid a = if a = voidTy then unitTy else a @@ -349,9 +264,9 @@ module Patterns = let (a, b) = removeVoid a, removeVoid b funTyC.MakeGenericType([| a;b |]) - let mkArrayTy (t:System.Type) = t.MakeArrayType() + let mkArrayTy (t:Type) = t.MakeArrayType() + let mkExprTy (t:Type) = exprTyC.MakeGenericType([| t |]) let rawExprTy = typeof - let mkExprTy (t:System.Type) = rawExprTy //-------------------------------------------------------------------------- @@ -525,7 +440,7 @@ module Patterns = | LetRaw(e, Lambda(v, body)) -> Some(v, e, body) | _ -> None - let (|IteratedLambda|_|) (e: Expr) = Helpers.qOneOrMoreRLinear (|Lambda|_|) e + let (|IteratedLambda|_|) (e: Expr) = qOneOrMoreRLinear (|Lambda|_|) e let rec (|NLambdas|_|) n (e:Expr) = match e with @@ -536,7 +451,8 @@ module Patterns = [] let (|NewDelegate|_|) input = match input with - | Comb1(NewDelegateOp(ty, nargs), e) -> + | Comb1(NewDelegateOp(ty), e) -> + let nargs = getDelegateNargs ty if nargs = 0 then match e with | NLambdas 1 ([_], e) -> Some(ty, [], e) // try to strip the unit parameter if there is one @@ -554,195 +470,1720 @@ module Patterns = | 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) + match mems |> Array.tryFind (fun minfo -> minfo.Name = fieldName) with + | Some (m) -> m + | _ -> invalidArg "fieldName" (sprintf "missing record field for type %s %s" ty.FullName fieldName) + + let getUnionCaseInfo(ty, unionCaseName) = + let cases = FSharpType.GetUnionCases(ty) + match cases |> Array.tryFind (fun ucase -> ucase.Name = unionCaseName) with + | Some(case) -> case + | _ -> invalidArg "unionCaseName" (sprintf "missing union-case for %s: %s" ty.FullName unionCaseName) + + let getUnionCaseInfoField(unionCase:UnionCaseInfo, index) = + let fields = unionCase.GetFields() + if index < 0 || index >= fields.Length then invalidArg "index" (sprintf "invalid union-case-field index for %s: %d" unionCase.Name index) + 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 + | 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 + | InstancePropSetOp _, _ -> typeof + | InstanceFieldGetOp fld, _ -> fld.FieldType + | StaticFieldGetOp fld, _ -> fld.FieldType + | InstanceFieldSetOp _, _ -> typeof + | StaticFieldSetOp _, _ -> typeof + | NewObjectOp ctor, _ -> ctor.DeclaringType + | InstanceMethodCallOp minfo, _ -> minfo.ReturnType |> removeVoid + | StaticMethodCallOp minfo, _ -> minfo.ReturnType |> removeVoid + | CoerceOp ty, _ -> ty + | SequentialOp, [_;b] -> typeOf b + | ForIntegerRangeLoopOp, _ -> typeof + | NewArrayOp ty, _ -> mkArrayTy ty + | NewDelegateOp ty, _ -> ty + | DefaultValueOp ty, _ -> ty + | TypeTestOp _, _ -> typeof + | QuoteOp true, [expr] -> mkExprTy (typeOf expr) + | QuoteOp false, [_] -> rawExprTy + | TryFinallyOp, [e1;_] -> typeOf e1 + | TryWithOp, [e1;_;_] -> typeOf e1 + | WhileLoopOp, _ + | VarSetOp, _ + | AddressSetOp, _ -> typeof + | AddressOfOp, [expr]-> failwith "(typeOf expr).MakeByRefType()" + | (AddressOfOp | QuoteOp _ | SequentialOp | TryWithOp | TryFinallyOp | IfThenElseOp | AppOp), _ -> failwith "unreachable" + //-------------------------------------------------------------------------- + // Constructors for building Raw quotations + //-------------------------------------------------------------------------- -module PatternsOld = + 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 - let inline (|E|) (e : Expr) = 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 + //-------------------------------------------------------------------------- + // Type-checked constructors for building Raw quotations + //-------------------------------------------------------------------------- - [] - let (|Value|_|) (e : Expr) = - match e with - | E (CombTerm(ValueOp(v, t, _), [])) -> Some (v,t) + // // 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) = + 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) + 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(box v, ty, None) + let mkUnit () = mkValue(null, typeof) + 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) = + let mems = FSharpType.GetRecordFields(ty) + 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) = + if Unchecked.defaultof = unionCase then raise (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 = unionCase then raise (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) (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 = finfo then raise (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 = finfo then raise (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 = finfo then raise (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 = finfo then raise (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) = + if Unchecked.defaultof = ci then raise (ArgumentNullException()) + checkArgs (ci.GetParameters()) args + mkFEN (NewObjectOp ci) args + + let mkDefaultValue (ty:Type) = + mkFE0 (DefaultValueOp ty) + + let mkStaticPropGet (pinfo:PropertyInfo, args:list) = + if Unchecked.defaultof = pinfo then raise (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) = + if Unchecked.defaultof = pinfo then raise (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, value:Expr) = + if Unchecked.defaultof = pinfo then raise (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, value:Expr) = + if Unchecked.defaultof = pinfo then raise (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) = + if Unchecked.defaultof = minfo then raise (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) = + if Unchecked.defaultof = minfo then raise (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) (typeOf lowerBound) "lowerBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") + checkTypesSR (typeof) (typeOf upperBound) "upperBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") + checkTypesSR (typeof) (v.Type) "for" ("SR.GetString(SR.QtmmLoopBodyMustBeLambdaTakingInteger)") + mkFE3 ForIntegerRangeLoopOp (lowerBound, upperBound, mkLambda(v, body)) + + let mkWhileLoop (guard, body) = + checkTypesSR (typeof) (typeOf guard) "guard" ("SR.GetString(SR.QtmmGuardMustBeBool)") + checkTypesSR (typeof) (typeOf body) "body" ("SR.GetString(SR.QtmmBodyMustBeUnit)") + mkFE2 (WhileLoopOp) (guard, body) + + let mkNewDelegate (ty, e) = + let nargs = getDelegateNargs ty + let targs = ty.GetGenericArguments() + let ps = targs |> Array.take nargs + let ret = targs.[targs.Length - 1] + let dlfun = Array.foldBack mkFunTy ps ret + 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>) = 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. + [] + type ModuleDefinitionBindingResult<'T, 'R> = + | Unique of 'T + | Ambiguous of 'R + + let typeEquals (s:Type) (t:Type) = s = 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() |> 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 = + match parentT.GetMethod(nm, argTs) with + | null -> None + | res -> Some(res) + 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) 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() |> 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 + match ty.GetMethod(nm, argTypes) with + | null -> None + | res -> Some(res) + with _ -> 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() + |> 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) + // 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 = typeof // TODO: find parameter //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 (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() + |> 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() + |> 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) |> 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) |> checkNonNullResult ("fldName", "String.Format(SR.GetString(SR.QfailedToBindField), fldName)") // fxcop may not see "fldName" as an arg + + let bindGenericCctor (tc:Type) = + tc.GetConstructor([| |]) + |> checkNonNullResult ("tc", "SR.GetString(SR.QfailedToBindConstructor)") + + let bindGenericCtor (tc:Type, argTypes:Instantiable) = + 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(Array.ofList argtyps) |> checkNonNullResult ("tc", "SR.GetString(SR.QfailedToBindConstructor)") +#endif + + let bindCtor (tc, argTypes:Instantiable, 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(Array.ofList argtyps) |> 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>, retType, methName, numMethTyargs) = + bindMethodHelper(tc, methName, numMethTyargs, argTypes, retType) + + let bindMeth ((tc:Type, argTypes : list>, 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 = + + [] + type InputState = + { is: ByteStream + istrings: string[] + referencedTypeDefs: Type[] } + + let u_byte_as_int st = st.is.ReadByte() + + let u_bool st = + let b = u_byte_as_int st + (b = 1) + + let u_void (_: 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 (|Var|_|) (e : Expr) = - match e with - | E (VarTerm(v)) -> Some (v) - | _ -> None + let u_bytes st = + let len = u_int32 st + st.is.ReadBytes len + let prim_u_string st = + let len = u_int32 st + st.is.ReadUtf8BytesAsString len - [] - let (|Lambda|_|) (e : Expr) = - match e with - | E(LambdaTerm(v, b)) -> Some (v,b) - | _ -> None + let u_int st = u_int32 st - [] - let (|Application|_|) (e : Expr) = - match e with - | E(CombTerm(AppOp, [f;e])) -> Some (f, e) - | _ -> None + let u_sbyte st = sbyte (u_int32 st) - [] - let (|Let|_|) (e : Expr) = - match e with - | E(CombTerm(LetOp, [e; E(LambdaTerm(v, b))])) -> Some (v,e,b) - | _ -> None + let u_byte st = byte (u_byte_as_int st) + let u_int16 st = int16 (u_int32 st) - let private (|IteratedLambda|_|) (e: Expr) = Helpers.qOneOrMoreRLinear (|Lambda|_|) e + let u_uint16 st = uint16 (u_int32 st) - [] - let (|LetRecursive|_|) (e : Expr) = - match e.Tree with - | CombTerm(LetRecOp, [IteratedLambda(vs, E(CombTerm(LetRecCombOp, b2::bs)))]) -> Some(List.zip vs bs, b2) - | _ -> None + let u_uint32 st = uint32 (u_int32 st) - [] - let (|IfThenElse|_|) (e : Expr) = - match e with - | E(CombTerm(IfThenElseOp, [c; i; e])) -> Some (c,i,e) - | _ -> None + 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 (|NewRecord|_|) (e : Expr) = - match e with - | E(CombTerm(NewRecordOp t, args)) -> Some (t, args) - | _ -> None + let u_double st = System.BitConverter.ToDouble(System.BitConverter.GetBytes(u_int64 st), 0) - [] - let (|NewUnionCase|_|) (e : Expr) = - match e with - | E(CombTerm(NewUnionCaseOp c, args)) -> Some (c, args) - | _ -> None + let u_float32 st = System.BitConverter.ToSingle(System.BitConverter.GetBytes(u_int32 st), 0) - [] - let (|UnionCaseTest|_|) (e : Expr) = - match e with - | E(CombTerm(UnionCaseTestOp c, [arg])) -> Some (arg, c) - | _ -> None + let u_char st = char (int32 (u_uint16 st)) - [] - let (|NewTuple|_|) (e : Expr) = - match e with - | E(CombTerm(NewTupleOp _, args)) -> Some(args) - | _ -> None + let inline u_tup2 p1 p2 st = let a = p1 st in let b = p2 st in (a, b) - [] - let (|TupleGet|_|) (e : Expr) = - match e with - | E(CombTerm(TupleGetOp(_, i), [arg])) -> Some(arg, i) - | _ -> None + 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 (|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 + 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 (|FieldGet|_|) input = - match input with - | E(CombTerm(StaticFieldGetOp finfo, [])) -> Some(None, finfo) - | E(CombTerm(InstanceFieldGetOp finfo, [obj])) -> Some(Some(obj), finfo) - | _ -> None + 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 (|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 + let u_list f st = u_list_aux f [] st - [] - 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 + let unpickleObj referencedTypeDefs u phase2bytes = + let phase2data = + let st2 = + { is = new ByteStream(phase2bytes, 0, phase2bytes.Length) + istrings = [| |] + 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 + referencedTypeDefs=referencedTypeDefs } + let res = u st1 + res - [] - let (|Coerce|_|) (e : Expr) = - match e with - | E(CombTerm(CoerceOp(t), [a])) -> Some(a,t) - | _ -> None + 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.MakeArrayType(1) returns "Int[*]" but we need "Int[]" + | _ -> invalidArg "tys" ("SR.GetString(SR.QexpectedOneType)") + + // let mkNamedTycon (tcName, assembly:Assembly) = + // match assembly.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 (assembly.GetTypes() |> Array.tryFind (fun a -> a.FullName = tcName)) with + // | Some ty -> ty + // | None -> invalidArg "tcName" ("String.Format(SR.GetString(SR.QfailedToBindTypeInAssembly), tcName, assembly.FullName)") // "Available types are:\n%A" tcName assembly (assembly.GetTypes() |> Array.map (fun a -> a.FullName)) + // | ty -> ty + + let decodeNamedTy tc tsR = mkNamedType (tc, tsR) + + let u_assemblyRef 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())") +// | assembly -> assembly + + let u_NamedType st = + let a, b = u_tup2 u_string u_assemblyRef st + + match System.Int32.TryParse(a) with + | (true, idx) when b = "" -> + // 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 + st.referencedTypeDefs.[idx] + | _ -> + // 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, @"(? u_void 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" + + [] + type BindingEnv = + { /// Mapping from variable index to Var object for the variable + vars : Map + /// 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 -> + let a = u_constSpec st + let b = u_dtypes st + let args = u_list u_Expr st + (fun (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 + (x >> VarTerm >> E) + | 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 -> 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)) + | 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 referencedTypes u_Expr bytes + + let unpickleReflectedDefns referencedTypes bytes = + unpickleObj referencedTypes u_ReflectedDefinitions bytes - [] - let (|NewArray|_|) (e : Expr) = - match e with - | E(CombTerm(NewArrayOp(t), args)) -> Some(t, args) - | _ -> None + //-------------------------------------------------------------------------- + // 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 + + [] + 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 = 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 mutable i = 0 + // while (i < n) do + // i <- i + s.Read(res, i, (n - i)) + // res + + let decodedTopResources = new Dictionary(10, HashIdentity.Structural) + +#if !FX_NO_REFLECTION_METADATA_TOKENS +#if FX_NO_REFLECTION_MODULE_HANDLES // not available on Silverlight + [] + 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 + [] + 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() 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 + [] + type ReflectedDefinitionTableKey = + | Key of string + static member GetKey(methodBase:MethodBase) = + // TODO: better key + Key (methodBase.DeclaringType.FullName + "." + methodBase.Name) + //Key(methodBase.Module.ModuleHandle, methodBase.MetadataToken) +#endif + + [] + type ReflectedDefinitionTableEntry = Entry of Bindable + + let reflectedDefinitionTable = new Dictionary(10, HashIdentity.Structural) + + let registerReflectedDefinitions (resourceName, bytes, referencedTypes) = + let defns = unpickleReflectedDefns referencedTypes bytes + defns |> List.iter (fun (minfo, exprBuilder) -> + let key = ReflectedDefinitionTableKey.GetKey minfo + lock reflectedDefinitionTable (fun () -> + reflectedDefinitionTable.Add(key, Entry(exprBuilder)))) + decodedTopResources.Add((resourceName), 0) + + let tryGetReflectedDefinition (methodBase: MethodBase, tyargs: Type []) = + checkNonNull "methodBase" methodBase + let data = + 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) |> Seq.toArray +// #else +// assem.GetCustomAttributes(typeof, 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 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) - let rec private (|NLambdas|_|) n (e:Expr) = + 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: obj, expressionType: Type) = + checkNonNull "expressionType" expressionType + mkValue(value, expressionType) + + + [] + static member ValueWithName(value: obj, expressionType: Type, name:string) = + checkNonNull "expressionType" expressionType + checkNonNull "name" name + mkValueWithName(value, expressionType, name) + + [] + 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(resource, serializedValue) = + Expr.RegisterReflectedDefinitions(resource, serializedValue, [| |]) + + static member RegisterReflectedDefinitions(resource, serializedValue, referencedTypes) = + registerReflectedDefinitions( resource, serializedValue, referencedTypes) + + static member GlobalVar<'T>(name) : Expr<'T> = + checkNonNull "name" name + Expr.Var(Var.Global(name, typeof<'T>)) |> Expr.Cast + +[] +module DerivedPatterns = + open Patterns + + [] + let (|Bool|_|) input = match input with ValueObj(:? bool as v) -> Some(v) | _ -> None + [] + let (|String|_|) input = match input with ValueObj(:? string as v) -> Some(v) | _ -> None + [] + let (|Single|_|) input = match input with ValueObj(:? single as v) -> Some(v) | _ -> None + [] + let (|Double|_|) input = match input with ValueObj(:? double as v) -> Some(v) | _ -> None + [] + let (|Char|_|) input = match input with ValueObj(:? char as v) -> Some(v) | _ -> None + [] + let (|SByte|_|) input = match input with ValueObj(:? sbyte as v) -> Some(v) | _ -> None + [] + let (|Byte|_|) input = match input with ValueObj(:? byte as v) -> Some(v) | _ -> None + [] + let (|Int16|_|) input = match input with ValueObj(:? int16 as v) -> Some(v) | _ -> None + [] + let (|UInt16|_|) input = match input with ValueObj(:? uint16 as v) -> Some(v) | _ -> None + [] + let (|Int32|_|) input = match input with ValueObj(:? int32 as v) -> Some(v) | _ -> None + [] + let (|UInt32|_|) input = match input with ValueObj(:? uint32 as v) -> Some(v) | _ -> None + [] + let (|Int64|_|) input = match input with ValueObj(:? int64 as v) -> Some(v) | _ -> None + [] + let (|UInt64|_|) input = match input with ValueObj(:? uint64 as v) -> Some(v) | _ -> None + [] + let (|Unit|_|) input = match input with Comb0(ValueOp(_, ty, None)) when ty = typeof -> 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 - | _ when n <= 0 -> Some([], e) - | Lambda(v, NLambdas ((-) n 1) (vs, b)) -> Some(v::vs, b) + | Application(f, x) -> + match x with + | Unit -> Some(f, []) + | NewTuple(x) -> Some(f, x) + | x -> Some(f, [x]) | _ -> None - [] - let (|NewDelegate|_|) input = + [] + let (|Lambdas|_|) (input: Expr) = qOneOrMoreRLinear (|TupledLambda|_|) input + [] + let (|Applications|_|) (input: Expr) = qOneOrMoreLLinear (|TupledApplication|_|) input + /// Reverse the compilation of And and Or + [] + let (|AndAlso|_|) input = match input with - | CombTerm(NewDelegateOp(ty, nargs), [e]) -> - 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 - - - [] - let (|Quote|_|) (E x) = match x with CombTerm(QuoteOp _, [a]) -> Some (a) | _ -> None + | IfThenElse(x, y, Bool(false)) -> Some(x, y) + | _ -> None - [] - let (|QuoteRaw|_|) (E x) = match x with CombTerm(QuoteOp false, [a]) -> Some (a) | _ -> None + [] + let (|OrElse|_|) input = + match input with + | IfThenElse(x, Bool(true), y) -> Some(x, y) + | _ -> None - [] - let (|QuoteTyped|_|) (E x) = match x with CombTerm(QuoteOp true, [a]) -> Some (a) | _ -> None + [] + 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) + when ( // if metadata tokens are not available we'll rely only on equality of method references + 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 + + // [] + // 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 + + [] + let (|MethodWithReflectedDefinition|_|) (methodBase) = + Expr.TryGetReflectedDefinition(methodBase) + + [] + let (|PropertyGetterWithReflectedDefinition|_|) (propertyInfo:System.Reflection.PropertyInfo) = + Expr.TryGetReflectedDefinition(propertyInfo.GetGetMethod(true)) + + [] + let (|PropertySetterWithReflectedDefinition|_|) (propertyInfo:System.Reflection.PropertyInfo) = + Expr.TryGetReflectedDefinition(propertyInfo.GetSetMethod(true)) - [] - let (|DefaultValue|_|) input = match input with E(CombTerm(DefaultValueOp(ty), [])) -> Some(ty) | _ -> None - // static member Quote(e : Expr) = - // Expr(CombTerm(QuoteOp true, [e])) - // static member QuoteRaw(e : Expr) = - // Expr(CombTerm(QuoteOp false, [e])) - // static member QuoteTyped(e : Expr) = - // Expr(CombTerm(QuoteOp false, [e])) - // static member Sequential(l : Expr, r : Expr) = - // Expr(CombTerm(SequentialOp, [l;r])) - // static member TryWith(body : Expr, filterVar : Var, filterBody : Expr, catchVar : Var, catchExpr : Expr) = - // Expr(CombTerm(TryWithOp, [body; Expr.Lambda(filterVar, filterBody); Expr.Lambda(catchVar, catchExpr)])) - // static member TryFinally(body : Expr, compensation : Expr) = - // Expr(CombTerm(TryFinallyOp, [body; compensation])) - // static member ForIntegerRangeLoop(i : Var, s : Expr, e : Expr, body : Expr) = - // Expr(CombTerm(ForIntegerRangeLoopOp, [s;e;Expr.Lambda(i, body)])) - // static member WhileLoop(cond : Expr, body : Expr) = - // Expr(CombTerm(WhileLoopOp, [cond; body])) - // static member VarSet(v : Var, value : Expr) = - // Expr (CombTerm(VarSetOp,[Expr.Var v; value])) - // static member AddressSet(addr : Expr, value : Expr) = - // Expr (CombTerm(AddressSetOp,[addr; value])) - // static member TypeTest(source : Expr, target : System.Type) = - // Expr (CombTerm(TypeTestOp target,[source])) - // static member DefaultValue(t : System.Type) = - // Expr (CombTerm(DefaultValueOp t, [])) - // static member WithValue(value: obj, expressionType: System.Type, definition: Expr) = - // Expr (CombTerm(WithValueOp(value, expressionType), [definition])) \ No newline at end of file +[] +module ExprShape = + open Patterns + let RebuildShapeCombination(shape:obj, arguments) = + // preserve the attributes + let op, attrs = unbox(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) + + [] + 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 (op, expr.CustomAttributes), args) + | HoleTerm _ -> invalidArg "expr" ("SR.GetString(SR.QunexpectedHole)") + loop (input :> Expr) \ No newline at end of file diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 7cce1ec373..fcd5e920fe 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -154,6 +154,10 @@ export class NMethodInfo extends NMethodBase { public get_IsGenericMethod() { return this.isGenericDef || this.declaration; } + public GetGenericArguments(): NTypeInfo[] { + return this.GenericArguments; + } + public GetGenericMethodDefinition(): NMethodInfo { if (this.declaration) { return this.declaration; @@ -287,6 +291,18 @@ export class NPropertyInfo extends NMemberInfo { public get_IsStatic() { return this.IsStatic; } public get_IsFSharp() { return this.IsFSharp; } + public get_CanRead() { + return this.get_GetMethod() !== null; + } + public get_CanWrite() { + return this.get_SetMethod() !== null; + } + + public GetIndexParameters() { + const m = this.get_GetMethod(); + if (m) { return m.GetParameters(); } else { return []; } + } + public get_GetMethod() { const getterName = "get_" + this.Name; const mems = this.DeclaringType.GetAllMembers(); @@ -410,19 +426,20 @@ export class NTypeInfo { if (NTypeInfo.parameterCache[i]) { return NTypeInfo.parameterCache[i]; } else { - const p = new NTypeInfo(i, 0, true, [], (_s) => []); + const p = new NTypeInfo(i, 0, true, [], (_s) => [], null, null); this.parameterCache[i] = p; return p; } } public static Simple(name: string) { - return new NTypeInfo(name, 0, false, [], ((_s) => [])); + return new NTypeInfo(name, 0, false, [], ((_s) => []), null, null); } private static parameterCache: {[i: string]: NTypeInfo} = {}; public generics: NTypeInfo[] = null; - public declaration: NTypeInfo = null; + public GenericDeclaration: NTypeInfo = null; + public DeclaringType: NTypeInfo = null; private instantiations: {[i: string]: NTypeInfo} = {}; private mems: NMemberInfo[] = null; @@ -434,7 +451,8 @@ export class NTypeInfo { public isGenericParameter: boolean, _generics: NTypeInfo[], public members: (self: NTypeInfo) => NMemberInfo[], - decl?: NTypeInfo) { + genericDeclaration: NTypeInfo, + declaringType: NTypeInfo) { if (!fullname) { throw new Error("cannot declare type without name"); } members = members || ((_s) => []); _generics = _generics || []; @@ -444,8 +462,8 @@ export class NTypeInfo { const g = _generics.filter((t) => !t.isGenericParameter); if (g.length === genericCount) { this.generics = g; - if (decl) { - this.genericMap = decl.genericMap; + if (genericDeclaration) { + this.genericMap = genericDeclaration.genericMap; } } else { _generics.forEach((g, i) => { @@ -454,13 +472,22 @@ export class NTypeInfo { this.generics = _generics; } if (this.generics.length !== this.genericCount) { throw new Error(`${this.fullname} contains ${this.genericCount} generic parameters but only ${this.generics.length} given.`); } - this.declaration = decl || null; + this.GenericDeclaration = genericDeclaration || null; + this.DeclaringType = declaringType; + } + + public get_DeclaringType(): NTypeInfo { + return this.DeclaringType; } public MakeArrayType(): NTypeInfo { return array(this); } + public get_IsGenericParameter() { + return this.isGenericParameter; + } + public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genericMap) { @@ -470,7 +497,7 @@ export class NTypeInfo { return t; } } else if (t.genericCount > 0) { - return new NTypeInfo(t.fullname, t.genericCount, false, t.generics.map((ta) => this.ResolveGeneric(ta)), t.members, t.declaration); + return new NTypeInfo(t.fullname, t.genericCount, false, t.generics.map((ta) => this.ResolveGeneric(ta)), t.members, t.GenericDeclaration, t.DeclaringType); } else { return t; } @@ -479,8 +506,8 @@ export class NTypeInfo { public GetGenericTypeDefinition() { if (this.genericCount === 0 || this.get_IsGenericTypeDefinition()) { return this; - } else if (this.declaration) { - return this.declaration; + } else if (this.GenericDeclaration) { + return this.GenericDeclaration; } else { throw new Error(`${this.fullname} does not have a proper generic definition`); } @@ -495,7 +522,7 @@ export class NTypeInfo { if (key in this.instantiations) { return this.instantiations[key]; } else { - const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this); + const res = new NTypeInfo(this.fullname, this.genericCount, this.isGenericParameter, args, this.members, this, this.DeclaringType); this.instantiations[key] = res; return res; } @@ -577,6 +604,26 @@ export class NTypeInfo { const m = this.GetAllMembers(); return m.filter((m) => m instanceof NMethodInfo) as NMethodInfo[]; } + public GetConstructors() { + const m = this.GetAllMembers(); + return m.filter((m) => m instanceof NConstructorInfo) as NConstructorInfo[]; + } + public GetFields() { + const m = this.GetAllMembers(); + return m.filter((m) => m instanceof NFieldInfo) as NFieldInfo[]; + } + public GetConstructor(ts?: NTypeInfo[]) { + if (ts) { + return this.GetConstructors().find((ctor) => ctor.ParametersAssignable(ts)); + } else { + const ctors = this.GetConstructors(); + return ctors.length === 1 ? ctors[0] : null; + } + } + public GetField(name: string) { + const m = this.GetAllMembers(); + return m.find((m) => m instanceof NFieldInfo && m.Name === name) as NFieldInfo; + } public GetProperty(name: string) { const m = this.GetAllMembers(); @@ -653,7 +700,7 @@ export function declareNType(fullname: string, generics: number, members: (self: const pars = Array.from({ length: generics }, (_, i) => getGenericParameter("a" + i)); const mems = members || ((_self, _gen) => []); - gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => mems(s, pars)); + gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => mems(s, pars), null, null); typeCache[fullname] = gen; } return gen; @@ -694,7 +741,7 @@ export function compare(t1: NTypeInfo, t2: NTypeInfo): number { } } -export function ntype(fullname: string, genericNames?: string[], generics?: NTypeInfo[], members?: (self: NTypeInfo, pars: NTypeInfo[]) => NMemberInfo[]): NTypeInfo { +export function ntype(fullname: string, genericNames?: string[], generics?: NTypeInfo[], members?: (self: NTypeInfo, pars: NTypeInfo[]) => NMemberInfo[], declaringType?: NTypeInfo): NTypeInfo { let gen: NTypeInfo = null; generics = generics || []; const a = generics.findIndex((t) => !t); @@ -705,11 +752,12 @@ export function ntype(fullname: string, genericNames?: string[], generics?: NTyp } else { members = members || ((_s, _g) => []); genericNames = genericNames || []; + declaringType = declaringType || null; const b = genericNames.findIndex((t) => !t); if (b >= 0) { throw new Error("bad hate occured"); } const pars = genericNames.map((n) => getGenericParameter(n)); - gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars)); + gen = new NTypeInfo(fullname, pars.length, false, pars, (s) => members(s, pars), null, declaringType); typeCache[fullname] = gen; } @@ -850,6 +898,31 @@ export const decimal: NTypeInfo = NTypeInfo.Simple("System.Decimal"); // FSharpType +export function isType(o: any) { + return o instanceof NTypeInfo; +} +export function isMemberInfo(o: any) { + return o instanceof NMemberInfo; +} +export function isMethodBase(o: any) { + return o instanceof NMethodBase; +} +export function isMethodInfo(o: any) { + return o instanceof NMethodInfo; +} +export function isPropertyInfo(o: any) { + return o instanceof NPropertyInfo; +} +export function isUnionCaseInfo(o: any) { + return o instanceof NUnionCaseInfo; +} +export function isFieldInfo(o: any) { + return o instanceof NFieldInfo; +} +export function isConstructorInfo(o: any) { + return o instanceof NConstructorInfo; +} + export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { const cases = t.GetAllMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; if (cases.length > 0) { diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index a34524bb7b..86b9b7a8e4 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -167,8 +167,8 @@ let tests = | _ -> failwith "bad record" - // testCase "CustomAttributes" <| fun () -> + testCase "CustomAttributes" <| fun () -> - // let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) - // failwithf "prop: %s" (prop.ToPrettyString()) + let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) + failwithf "prop: %s" (prop.DeclaringType.Name) ] \ No newline at end of file From d344a0501056f92318db64cbe354073c3b7ae888 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sat, 13 Apr 2019 11:13:52 +0200 Subject: [PATCH 14/38] implemented quotation path through all stages (only Lambda/Var/Value/Let working atm.) --- src/Fable.Transforms/AST/AST.Fable.fs | 25 +- src/Fable.Transforms/FSharp2Fable.fs | 44 ++-- src/Fable.Transforms/Fable.Transforms.fsproj | 1 + src/Fable.Transforms/Fable2Babel.fs | 65 ++--- src/Fable.Transforms/FableTransforms.fs | 4 +- src/Fable.Transforms/QuotationPickler.fs | 235 +++++++++++++++++++ src/fable-library/ExprUtils.fs | 74 +++++- tests/Main/ExprTests.fs | 7 +- 8 files changed, 379 insertions(+), 76 deletions(-) create mode 100644 src/Fable.Transforms/QuotationPickler.fs diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 034cdcd2aa..e70211d1de 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -306,6 +306,21 @@ type DelayedResolutionKind = | AsPojo of Expr * caseRules: Expr | Curry of Expr * arity: int + +type VarData = + { name : string; typ : Type; isMutable : bool } + +type ValueData = + { name : string; typ : Type; expr : Expr } + +type ExprData = + { + typ : Type + variables: VarData[] + values : ValueData[] + data : byte[] + } + type Expr = | Value of ValueKind * SourceLocation option | IdentExpr of Ident @@ -335,11 +350,11 @@ type Expr = | TryCatch of body: Expr * catch: (Ident * Expr) option * finalizer: Expr option * range: SourceLocation option | IfThenElse of guardExpr: Expr * thenExpr: Expr * elseExpr: Expr * range: SourceLocation option - | Quote of typed : bool * value : Expr + | Quote of typed : bool * data : ExprData member this.Type = match this with - | Quote(true, value) -> Expr(Some value.Type) + | Quote(true, value) -> Expr(Some value.typ) | Quote(false, _) -> Expr None | Test _ -> Boolean | Value(kind,_) -> kind.Type @@ -358,9 +373,9 @@ type Expr = match this with | Import _ | DelayedResolution _ | ObjectExpr _ | Sequential _ | Let _ - | DecisionTree _ | DecisionTreeSuccess _ -> None - - | Quote(_,e) | Function(_,e,_) | TypeCast(e,_) -> e.Range + | DecisionTree _ | DecisionTreeSuccess _ | Quote _ -> None + + | Function(_,e,_) | TypeCast(e,_) -> e.Range | IdentExpr id -> id.Range | Value(_,r) | IfThenElse(_,_,_,r) | TryCatch(_,_,_,r) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index f3a5212815..fdd573c29f 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -737,27 +737,33 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = | BasicPatterns.Quote expr -> - - - // let rec test (e : FSharpExpr) = - // trampoline { - // match e with - // | BasicPatterns.Call(None, m, targs, margs, args) -> - // let! args = trampolineListMap test args - - - - // return failwith "" - // | _ -> - // return failwith "" - - // } - - + let data = QuotationPickler.serialize expr + data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) + + let fableData = + { + Fable.ExprData.typ = makeType com ctx.GenericArgs data.typ + Fable.variables = + data.variables |> Array.map (fun v -> + { + Fable.VarData.name = v.name; + Fable.VarData.typ = makeType com ctx.GenericArgs v.typ + Fable.VarData.isMutable = v.isMutable + } + ) + Fable.values = + data.values |> Array.map (fun v -> + { + Fable.ValueData.name = v.DisplayName + Fable.ValueData.typ = makeType com ctx.GenericArgs v.FullType + Fable.ValueData.expr = makeValueFrom com ctx None v + } + ) + Fable.data = data.data + } - let! expr = transformExpr com ctx expr - return Fable.Quote(false, expr) + return Fable.Quote(false, fableData) // return "Quotes are not currently supported by Fable" // |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) diff --git a/src/Fable.Transforms/Fable.Transforms.fsproj b/src/Fable.Transforms/Fable.Transforms.fsproj index 10ae22fd44..4a9af0055c 100644 --- a/src/Fable.Transforms/Fable.Transforms.fsproj +++ b/src/Fable.Transforms/Fable.Transforms.fsproj @@ -16,6 +16,7 @@ + diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 5cba9e089e..e440165e26 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -1317,50 +1317,27 @@ module Util = | Fable.Debugger _ | Fable.Throw _ | Fable.Loop _ | Fable.TryCatch _ -> iife com ctx expr :> Expression - | Fable.Quote(_,expr) -> - let serializeExpr (expr : Fable.Expr) = - // TODO: proper serialization - string expr - - // let mk (name : string) (args : list) = - // coreLibCall com ctx None "ExprUtil" ("mk" + name) (List.toArray args) - - // let ident = - // { - // Fable.Ident.Name = "Expr" - // Fable.Ident.Type = Fable.Type.Expr None - // Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent - // Fable.Ident.IsMutable = false - // Fable.Ident.IsCompilerGenerated = false - // Fable.Ident.Range = None - // } - - - - - - - // let rec visit (variables : System.Collections.Generic.Dictionary) (e : Fable.Expr) = - // match e with - // | Fable.Function(Fable.Lambda a, b, name) -> - // variables.[a.Name] <- a - // coreLibCall com ctx None "ExprUtil" "mkLambda" [| Identifier(a.Name, ?loc = a.Range); visit variables b |] - - // | Fable.Operation(Fable.OperationKind.Call(call, info), t, r) -> - // failwith "" - - // // //| Operation of OperationKind * typ: Type * range: SourceLocation option - // // let a = Fable.Get(Fable.IdentExpr ident, Fable.GetKind.FieldGet("Function", false, Fable.FunctionType(Fable.Function)), Fable.Function, None) - // // Fable.Operation(Fable.StaticCall (Fable.Get(Fable.IdentExpr ident, Fable.GetKind.FieldGet "Function")) , Fable.Type.Expr None, None) - // // failwith "" - - // | _ -> - // failwith "" - - - - let literal = StringLiteral(serializeExpr expr) - coreLibCall com ctx None "ExprUtils" "deserialize" [| literal |] + | Fable.Quote(_,data) -> + let obj (values : list) = + values |> List.toArray |> Array.map (fun (n,v) -> U3.Case1 (ObjectProperty(StringLiteral n, v))) |> ObjectExpression :> Expression + let values = + data.values |> Array.map (fun v -> + obj [ + "name", StringLiteral v.name :> Expression + "typ", transformAsExpr com ctx (Fable.Value(Fable.TypeInfo v.typ, None)) + "value", transformAsExpr com ctx v.expr + ] + ) + let vars = data.variables |> Array.map (fun (v : Fable.VarData) -> + obj [ + "name", StringLiteral v.name :> Expression + "typ", transformAsExpr com ctx (Fable.Value(Fable.TypeInfo v.typ, None)) + "isMutable", BooleanLiteral v.isMutable :> Expression + ] + ) + // let arrName = getTypedArrayName com NumberKind.UInt8 + // let expr = NewExpression(StringLiteral arrName, data.data |> Array.map (fun v -> NumericLiteral (float v) :> Expression)) + coreLibCall com ctx None "ExprUtils" "deserialize" [| ArrayExpression values; ArrayExpression vars; StringLiteral (System.Convert.ToBase64String data.data) |] let rec transformAsStatements (com: IBabelCompiler) ctx returnStrategy (expr: Fable.Expr): Statement array = diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 1d3ee98056..bafba7b9d9 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -7,7 +7,7 @@ open FSharp.Compiler.SourceCodeServices // TODO: Use trampoline here? let visit f e = match e with - | Quote(b, e) -> Quote(b, f e) + | Quote(b, e) -> Quote(b, e) | IdentExpr _ | Debugger _ -> e | TypeCast(e, t) -> TypeCast(f e, t) | Import(e1, e2, kind, t, r) -> Import(f e1, f e2, kind, t, r) @@ -110,7 +110,7 @@ let rec visitFromOutsideIn (f: Expr->Expr option) e = visit (visitFromOutsideIn f) e let getSubExpressions = function - | Quote(_,e) -> [e] + | Quote _ -> [] | IdentExpr _ | Debugger _ -> [] | TypeCast(e,_) -> [e] | Import(e1,e2,_,_,_) -> [e1;e2] diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs new file mode 100644 index 0000000000..5c77b140f5 --- /dev/null +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -0,0 +1,235 @@ +module Fable.Transforms.FSharp2Fable.QuotationPickler + +open System.Collections.Generic +open FSharp.Compiler.Ast +open FSharp.Compiler.SourceCodeServices +open System.IO + + +type PicklerState = + { + varId : int + variables : list + + valueId : int + values : list + + typeId : int + types : list + + + writer : BinaryWriter + } + +type State<'s, 'a> = { run : 's -> 's * 'a } +module State = + let get<'s, 'a> = { run = fun s -> s, s } + let put (s : 's) = { run = fun _ -> s, () } + let modify (f : 's -> 's) = { run = fun s -> f s, () } + + let map (f : 'a -> 'b) (m : State<'s, 'a>) = + { run = fun s -> + let s, a = m.run s + s, f a + } + let bind (f : 'a -> State<'s, 'b>) (m : State<'s, 'a>) = + { run = fun s -> + let (s,a) = m.run s + (f a).run s + } + + let value (v : 'a) = { run = fun s -> s, v } + + type StateBuilder() = + member x.Bind(m : State<'s, 'a>, f : 'a -> State<'s, 'b>) = bind f m + member x.Return v = value v + member x.ReturnFrom(s : State<'s, 'a>) = s +let state = State.StateBuilder() + +module List = + let rec mapS (f : 'a -> State<'s, 'b>) (l : list<'a>) = + match l with + | [] -> State.value [] + | h :: t -> + f h |> State.bind (fun h -> mapS f t |> State.map (fun t -> h :: t)) + +module Pickler = + let newVar (l : FSharpMemberOrFunctionOrValue) = + { run = fun s -> + { s with + varId = s.varId + 1 + variables = (l, s.varId) :: s.variables + }, s.varId + } + + let tryGetVar (l : FSharpMemberOrFunctionOrValue) = + State.get |> State.map (fun s -> + s.variables |> List.tryPick (fun (m, i) -> if m = l then Some i else None) + ) + + let getVar (l : FSharpMemberOrFunctionOrValue) = + tryGetVar l |> State.map Option.get + + let useValue (l : FSharpMemberOrFunctionOrValue) = + { run = fun s -> + let res = s.values |> List.tryPick (fun (v,i) -> if v = l then Some i else None) + match res with + | Some res -> + s, res + | None -> + let id = s.valueId + { s with valueId = id + 1; values = (l, id) :: s.values }, id + } + let useType (t : FSharpType) = + { run = fun s -> + let res = s.types |> List.tryPick (fun (v,i) -> if v = t then Some i else None) + match res with + | Some res -> + s, res + | None -> + let id = s.typeId + { s with typeId = id + 1; types = (t, id) :: s.types }, id + } + + type Writes private() = + static member Write(s : BinaryWriter, v : byte) = s.Write v + static member Write(s : BinaryWriter, v : byte[]) = s.Write v + static member Write(s : BinaryWriter, v : int8) = s.Write v + static member Write(s : BinaryWriter, v : uint16) = s.Write v + static member Write(s : BinaryWriter, v : int16) = s.Write v + static member Write(s : BinaryWriter, v : uint32) = s.Write v + static member Write(s : BinaryWriter, v : int32) = s.Write v + static member Write(s : BinaryWriter, v : string) = + let bytes = System.Text.Encoding.UTF8.GetBytes v + s.Write(bytes.Length) + s.Write bytes + static member Write(s : BinaryWriter, vs : seq) = + let vs = Seq.toArray vs + s.Write vs.Length + for v in vs do s.Write v + + + let inline private writeAux< ^a, ^b, ^c when (^a or ^b or ^c) : (static member Write : ^a * ^b -> unit)> (c : ^c) (a : ^a) (b : ^b) = + ((^a or ^b or ^c) : (static member Write : ^a * ^b -> unit) (a,b)) + + let inline write b = + let doit w b = writeAux Unchecked.defaultof w b + { run = fun s -> + doit s.writer b + s, () + } + +let bytes (v : int) = + System.BitConverter.GetBytes(v) +let mk (k : byte) (sub : byte[][]) = + [| + yield k + yield! System.BitConverter.GetBytes(sub.Length) + for s in sub do + yield! System.BitConverter.GetBytes(s.Length) + yield! s + |] + +// 1uy -> Lambda(var, body) +// 2uy -> Var(var) +// 3uy -> Closure(id) +// 4uy -> Let(var, e, b) + +let rec serializeS (expr : FSharpExpr) = + state { + match expr with + | BasicPatterns.Lambda(v, b) -> + let! var = Pickler.newVar v + do! Pickler.write 1uy + do! Pickler.write var + return! serializeS b + + | BasicPatterns.Value v -> + match! Pickler.tryGetVar v with + | Some var -> + do! Pickler.write 2uy + do! Pickler.write var + | None -> + let! var = Pickler.useValue v + do! Pickler.write 3uy + do! Pickler.write var + + + | BasicPatterns.Let((v, e), b) -> + let! var = Pickler.newVar v + do! Pickler.write 4uy + do! Pickler.write var + do! serializeS e + do! serializeS b + + // | BasicPatterns.Call(target, m, targs, margs, args) -> + // let decl = m.DeclaringEntity |> Option.get + + // let! tids = targs |> List.mapS Pickler.useType + // let! mids = margs |> List.mapS Pickler.useType + + // match target with + // | Some target -> + // do! Pickler.write 5uy + // do! Pickler.write (decl.TryGetFullCompiledName().Value) + // do! Pickler.write tids + // do! Pickler.write m.CompiledName + // do! Pickler.write mids + + // do! serializeS target + + + // return () + + // | None -> + + // return () + + | _ -> + let code = sprintf "BAD EXPRESSION: %A" expr + do! Pickler.write 255uy + do! Pickler.write (sprintf "BAD EXPRESSION: %A" expr) + } + +type VarData = + { name : string; typ : FSharpType; isMutable : bool } + +type ExprData = + { + typ : FSharpType + variables: VarData[] + values : FSharpMemberOrFunctionOrValue[] + data : byte[] + } + +let serialize (expr : FSharpExpr) = + let s = serializeS expr + use stream = new System.IO.MemoryStream() + use w = new System.IO.BinaryWriter(stream) + let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = [] } + w.Flush() + let data = stream.ToArray() + let variables = + s.variables + |> List.sortBy snd + |> List.map (fun (m,_) -> + { name = m.DisplayName; typ = m.FullType; isMutable = m.IsMutable } + ) + |> List.toArray + let values = + s.values + |> List.sortBy snd + |> List.map fst + |> List.toArray + { + typ = expr.Type + variables = variables + values = values + data = data + } + + + + + + diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index afac3db6dc..43fe407a4c 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -1,10 +1,78 @@ module ExprUtils open Microsoft.FSharp.Quotations +open Fable.Import.JS -let deserialize (str : string) = - let v = Var("a", typeof) - Expr.Let(v, Expr.Value(1, typeof), Expr.Var v) + +type IValue = + abstract member typ : System.Type + abstract member value : obj + abstract member name : string + +type IVariable = + abstract member typ : System.Type + abstract member name : string + abstract member isMutable : bool + +type BinaryStream(arr : Uint8Array) = + let view = DataView.Create(arr.buffer, arr.byteOffset, arr.byteLength) + let mutable position = 0 + + member x.ReadByte() = + let value = view.getUint8(float position) + position <- position + 1 + byte value + + member x.ReadInt32() = + let value = view.getInt32(float position, true) + position <- position + 4 + int value + + member x.ReadString() = + let length = x.ReadInt32() + let view = Uint8Array.Create(arr.buffer, arr.byteOffset + float position, float length) + let value = System.Text.Encoding.UTF8.GetString(unbox view) + position <- position + length + value + + +// 1uy -> Lambda(var, body) +// 2uy -> Var(var) +// 3uy -> Closure(id) +// 4uy -> Let(var, e, b) + +let deserialize (values : IValue[]) (variables : IVariable[]) (data : string) : Expr = + let arr = System.Convert.FromBase64String(data) + let stream = BinaryStream(unbox arr) + + let values = values |> FSharp.Collections.Array.map (fun v -> Expr.ValueWithName(v.value, v.typ, v.name)) + let variables = variables |> FSharp.Collections.Array.map (fun v -> Var(v.name, v.typ, v.isMutable)) + + let rec read () = + let tag = stream.ReadByte() + match tag with + | 1uy -> + let vid = stream.ReadInt32() + let body = read() + Expr.Lambda(variables.[vid], body) + | 2uy -> + let vid = stream.ReadInt32() + Expr.Var(variables.[vid]) + | 3uy -> + let vid = stream.ReadInt32() + values.[vid] + | 4uy -> + let vid = stream.ReadInt32() + let e = read() + let b = read() + Expr.Let(variables.[vid], e, b) + | 255uy -> + let str = stream.ReadString() + failwithf "unsupported expression: %s" str + | _ -> + failwith "invalid expression" + + read() let isExpr (o : obj) = match o with diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 86b9b7a8e4..9a17b96392 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -168,7 +168,8 @@ let tests = failwith "bad record" testCase "CustomAttributes" <| fun () -> - - let prop = typedefof> //.MakeGenericType([| typeof |]) //.GetProperty("x", System.Reflection.BindingFlags.NonPublic) - failwithf "prop: %s" (prop.DeclaringType.Name) + let b = 10 + let test = <@ fun (a : int) -> b @> + + failwithf "prop: %A" test ] \ No newline at end of file From 6ba809ee65750410d93f274269b31c550065fc16 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sat, 13 Apr 2019 11:27:09 +0200 Subject: [PATCH 15/38] added one-line ToString for Expr --- src/fable-library/Quotations.fs | 92 ++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index dc4e210e04..296f67e652 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -9,8 +9,6 @@ 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 @@ -196,14 +194,96 @@ and [] | _ -> false eq x.Tree y.Tree | _ -> false - + member x.GetLayout(long) = + let expr (e:Expr ) = e.GetLayout(long) + let exprs (es:Expr list) = es |> List.map expr + let parens ls = sprintf "(%s)" (String.concat ", " ls) + let pairL l1 l2 = sprintf "(%s, %s)" l1 l2 + let listL ls = sprintf "[%s]" (String.concat ", " ls) + let combTaggedL nm ls = sprintf "%s%s" nm (parens ls) + let combL nm ls = sprintf "%s%s" nm (parens ls) + let noneL = "None" + let someL e = sprintf "Some(%s)" (expr e) + let typeL (o: Type) = if long then o.FullName else o.Name + let objL (o: 'T) = sprintf "%A" o + let varL (v:Var) = 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 unionCase.Name) + let minfoL (minfo: MethodInfo) = if long then objL minfo else minfo.Name + let cinfoL (cinfo: ConstructorInfo) = if long then objL cinfo else cinfo.DeclaringType.Name + let pinfoL (pinfo: PropertyInfo) = if long then objL pinfo else pinfo.Name + let finfoL (finfo: FieldInfo) = if long then objL finfo else 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 + // let combL (name : string) (args : seq) = sprintf "%s(%s)" name (String.concat ", " args) + // let exprs (es : seq) = es |> Seq.map (fun e -> e.GetLayout(long)) + + 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; 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 = getDelegateNargs ty + 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) -> v.Name + | LambdaTerm(v, b) -> combL "Lambda" [varL v; expr b] + | HoleTerm _ -> "_" + | CombTerm(QuoteOp _, args) -> combL "Quote" (exprs args) + | _ -> failwithf "Unexpected term in layout %A" x.Tree override x.GetHashCode() = x.Tree.GetHashCode() - // override x.ToString() = x.ToString(false) + 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.ToString(full) = + x.GetLayout(full) From b84fd2661e32d440c08f31175edd80130621bf7d Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 16 Apr 2019 11:59:23 +0200 Subject: [PATCH 16/38] most quotation things working --- src/Fable.Transforms/AST/AST.Fable.fs | 11 +- src/Fable.Transforms/FSharp2Fable.fs | 236 +++++++----- src/Fable.Transforms/Fable2Babel.fs | 38 +- src/Fable.Transforms/QuotationPickler.fs | 439 ++++++++++++++++++++--- src/fable-library/ExprUtils.fs | 323 ++++++++++++++++- src/fable-library/Quotations.fs | 206 ++++++----- src/fable-library/Reflection.ts | 70 +++- src/fable-library/String.ts | 48 ++- tests/Main/ExprTests.fs | 78 +++- 9 files changed, 1175 insertions(+), 274 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index e70211d1de..d58be3f609 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -315,10 +315,13 @@ type ValueData = type ExprData = { - typ : Type - variables: VarData[] - values : ValueData[] - data : byte[] + typ : Type + variables : VarData[] + values : ValueData[] + literals : Expr[] + types : Type[] + members : array + data : byte[] } type Expr = diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index fdd573c29f..94eda20515 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -270,6 +270,100 @@ let rec private transformDecisionTargets (com: IFableCompiler) (ctx: Context) ac return! transformDecisionTargets com ctx ((idents, expr)::acc) tail } +let private skipAttribute (name : string) = + // TODO: skip all attributes where definiton not known??? + name.StartsWith "Microsoft.FSharp.Core" || name.StartsWith "System.Reflection" + +let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = + match a.AttributeType.TryFullName with + | Some fullname when not (skipAttribute fullname) -> + + let types, args = a.ConstructorArguments |> Seq.toArray |> Array.unzip + let ctor = + a.AttributeType.MembersFunctionsAndValues |> Seq.tryPick (fun m -> + if m.IsConstructor then + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.map (fun p -> p.Type)|> Seq.toArray + if args.Length = pars.Length then + if FSharp.Collections.Array.forall2 (fun ta tp -> tp = ta) types pars then Some m + else None + else + None + else + None + ) + match ctor with + | Some ctor -> + let args = + a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> + match v with + | :? string as str -> Fable.Value(Fable.StringConstant str, None) + | _ -> Fable.Value(Fable.StringConstant (string v), None) + ) + let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType + let x = makeCallFrom com ctx None typ false [] None args ctor + Some (fullname,x) + | _ -> + None + | _ -> + None + +let private transformUnionCases (com:IFableCompiler) ctx (cases : seq) = + cases |> Seq.toArray |> Array.map (fun c -> + let fields = + c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> + { + Fable.MemberInfo.Kind = Fable.Property(f.Name, makeType com ctx.GenericArgs f.FieldType, true, false) + Fable.Attributes = f.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + ) + + { Fable.UnionCaseInfo.Name = c.Name; Fable.UnionCaseInfo.Fields = fields } + ) + + +let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFunctionOrValue) = + + if m.IsEvent || m.IsEventAddMethod || m.IsEventRemoveMethod then + // TODO: support these? + None + elif m.IsValue then + Some { + Fable.MemberInfo.Kind = Fable.Field(m.CompiledName, makeType com ctx.GenericArgs m.FullType, not m.IsInstanceMember) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsProperty then + Some { + Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, makeType com ctx.GenericArgs m.ReturnParameter.Type, false, not m.IsInstanceMember) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsConstructor then + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> + { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + ) + + let mangledName = Helpers.getMemberDeclarationName com m + + Some { + Fable.MemberInfo.Kind = Fable.Constructor(pars, mangledName) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + + else // m.IsPropertyGetterMethod || m.IsPropertySetterMethod || m.IsValCompiledAsMethod || m.IsInstanceMember then + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> + { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + ) + let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type + let mangledName = Helpers.getMemberDeclarationName com m + let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) + Some { + Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + + + let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = trampoline { match fsExpr with @@ -738,7 +832,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = | BasicPatterns.Quote expr -> let data = QuotationPickler.serialize expr - data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) + //data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) let fableData = { @@ -759,6 +853,36 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = Fable.ValueData.expr = makeValueFrom com ctx None v } ) + + Fable.ExprData.types = + data.types |> Array.map (fun d -> + match d with + | Choice1Of2 t -> makeType com ctx.GenericArgs t + | Choice2Of2(d,targs) -> makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List targs) d + ) + + Fable.ExprData.members = + data.members |> Array.map (fun m -> + match m with + | QuotationPickler.MemberDescription.Member (m, targs, margs) -> + let d = m.DeclaringEntity.Value + let t = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List(targs)) d + let mem = transformMemberInfo com ctx m |> Option.get + let margs = margs |> List.toArray |> Array.map (makeType com ctx.GenericArgs) + d, t, mem, margs + | QuotationPickler.MemberDescription.UnionCase (c, targs) -> + let d = c.ReturnType.TypeDefinition + let t = makeType com ctx.GenericArgs c.ReturnType + let mem = transformUnionCaseAsMember com ctx c + d, t, mem, [||] + ) + + Fable.literals = + data.literals |> Array.map (fun (v,t) -> + let ft = makeType com ctx.GenericArgs t + Replacements.makeTypeConst (makeRangeFrom fsExpr) ft v + ) + Fable.data = data.data } @@ -794,99 +918,29 @@ let private isIgnoredMember (meth: FSharpMemberOrFunctionOrValue) = | Some ent -> isErasedEntity ent | None -> false) -let private skipAttribute (name : string) = - // TODO: skip all attributes where definiton not known??? - name.StartsWith "Microsoft.FSharp.Core" || name.StartsWith "System.Reflection" - -let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = - match a.AttributeType.TryFullName with - | Some fullname when not (skipAttribute fullname) -> - - let types, args = a.ConstructorArguments |> Seq.toArray |> Array.unzip - let ctor = - a.AttributeType.MembersFunctionsAndValues |> Seq.tryPick (fun m -> - if m.IsConstructor then - let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.map (fun p -> p.Type)|> Seq.toArray - if args.Length = pars.Length then - if FSharp.Collections.Array.forall2 (fun ta tp -> tp = ta) types pars then Some m - else None - else - None - else - None - ) - match ctor with - | Some ctor -> - let args = - a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> - match v with - | :? string as str -> Fable.Value(Fable.StringConstant str, None) - | _ -> Fable.Value(Fable.StringConstant (string v), None) - ) - let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType - let x = makeCallFrom com ctx None typ false [] None args ctor - Some (fullname,x) - | _ -> - None - | _ -> - None - -let private transformUnionCases (com:IFableCompiler) ctx (cases : seq) = - cases |> Seq.toArray |> Array.map (fun c -> - let fields = - c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> - { - Fable.MemberInfo.Kind = Fable.Property(f.Name, makeType com ctx.GenericArgs f.FieldType, true, false) - Fable.Attributes = f.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - ) - - { Fable.UnionCaseInfo.Name = c.Name; Fable.UnionCaseInfo.Fields = fields } - ) +let private transformUnionCaseAsMember (com: IFableCompiler) ctx (c : FSharpUnionCase) = + let mangledName = Helpers.unionCaseCompiledName c |> Option.defaultValue c.Name + + if c.ReturnType.HasTypeDefinition then + let ent = c.ReturnType.TypeDefinition + let tag = ent.UnionCases |> Seq.findIndex (fun ci -> ci.Name = c.Name) + let mangledTypeName = getEntityDeclarationName com ent + let fields = c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> f.Name, makeType com ctx.GenericArgs f.FieldType) + { + Fable.MemberInfo.Kind = Fable.UnionCaseConstructor(tag, c.Name, fields, mangledName, mangledTypeName) + Fable.Attributes = c.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + else + //let mangledTypeName = getEntityDeclarationName com ent + let fields = c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> f.Name, makeType com ctx.GenericArgs f.FieldType) + { + Fable.MemberInfo.Kind = Fable.UnionCaseConstructor(0, c.Name, fields, mangledName, mangledName) + Fable.Attributes = c.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FSharpEntity) = let realMembers = - ent.TryGetMembersFunctionsAndValues |> Seq.choose (fun m -> - - if m.IsEvent || m.IsEventAddMethod || m.IsEventRemoveMethod then - // TODO: support these? - None - elif m.IsValue then - Some { - Fable.MemberInfo.Kind = Fable.Field(m.CompiledName, makeType com ctx.GenericArgs m.FullType, not m.IsInstanceMember) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - elif m.IsProperty then - Some { - Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, makeType com ctx.GenericArgs m.ReturnParameter.Type, false, not m.IsInstanceMember) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - elif m.IsConstructor then - let pars = - m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> - { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } - ) - - let mangledName = Helpers.getMemberDeclarationName com m - - Some { - Fable.MemberInfo.Kind = Fable.Constructor(pars, mangledName) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - - else // m.IsPropertyGetterMethod || m.IsPropertySetterMethod || m.IsValCompiledAsMethod || m.IsInstanceMember then - let pars = - m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> - { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } - ) - let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type - let mangledName = Helpers.getMemberDeclarationName com m - let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) - Some { - Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - ) + ent.TryGetMembersFunctionsAndValues |> Seq.choose (transformMemberInfo com ctx) let special = if ent.IsFSharpRecord then diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index e440165e26..fc0e5de49d 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -1328,6 +1328,7 @@ module Util = "value", transformAsExpr com ctx v.expr ] ) + let vars = data.variables |> Array.map (fun (v : Fable.VarData) -> obj [ "name", StringLiteral v.name :> Expression @@ -1335,9 +1336,42 @@ module Util = "isMutable", BooleanLiteral v.isMutable :> Expression ] ) + + let types = data.types |> Array.map (fun t -> + transformAsExpr com ctx (Fable.Value(Fable.TypeInfo t, None)) + ) + + let members = data.members |> Array.map (fun (ent, t, m, margs) -> + let self = transformAsExpr com ctx (Fable.Value(Fable.TypeInfo t, None)) + let minst = margs |> Array.map (fun t -> transformAsExpr com ctx (Fable.Value(Fable.TypeInfo t, None))) + + let arr = transformMemberReflectionInfosNew com ctx None self (ArrayExpression [||]) ent [|m|] + let meth = arr.[0] + if margs.Length > 0 then + CallExpression(MemberExpression(meth, Identifier "MakeGenericMethod"), [| ArrayExpression minst |]) :> Expression + else + meth + + ) + + let literals = + data.literals |> Array.map (fun e -> + obj [ + "value", transformAsExpr com ctx e + "typ", transformAsExpr com ctx (Fable.Value(Fable.TypeInfo e.Type, None)) + ] + ) + // let arrName = getTypedArrayName com NumberKind.UInt8 - // let expr = NewExpression(StringLiteral arrName, data.data |> Array.map (fun v -> NumericLiteral (float v) :> Expression)) - coreLibCall com ctx None "ExprUtils" "deserialize" [| ArrayExpression values; ArrayExpression vars; StringLiteral (System.Convert.ToBase64String data.data) |] + // let expr = NewExpression(Identifier arrName, [| data.data |> Array.map (fun v -> NumericLiteral (float v) :> Expression) |> ArrayExpression |]) + coreLibCall com ctx None "ExprUtils" "deserialize" [| + ArrayExpression values + ArrayExpression vars + ArrayExpression types + ArrayExpression members + ArrayExpression literals + StringLiteral (System.Convert.ToBase64String data.data) + |] let rec transformAsStatements (com: IBabelCompiler) ctx returnStrategy (expr: Fable.Expr): Statement array = diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index 5c77b140f5..7d3e8680b1 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -5,6 +5,9 @@ open FSharp.Compiler.Ast open FSharp.Compiler.SourceCodeServices open System.IO +type MemberDescription = + | Member of FSharpMemberOrFunctionOrValue * list * list + | UnionCase of FSharpUnionCase * list type PicklerState = { @@ -14,9 +17,16 @@ type PicklerState = valueId : int values : list + literalId : int + literals : list + + memberId : int + members : list + typeId : int - types : list + types : list> * int> + cases : list * FSharpExpr>> writer : BinaryWriter } @@ -44,6 +54,26 @@ module State = member x.Bind(m : State<'s, 'a>, f : 'a -> State<'s, 'b>) = bind f m member x.Return v = value v member x.ReturnFrom(s : State<'s, 'a>) = s + member x.Zero() = value () + member x.Delay (f : unit -> State<'s, 'a>) = { run = fun s -> f().run s } + member x.Combine(l : State<'s, unit>, r : State<'s, 'a>) = l |> bind (fun () -> r) + member x.For(seq : seq<'a>, action : 'a -> State<'s, unit>) = + { run = fun s -> + let mutable s = s + for e in seq do + let (s1, ()) = (action e).run s + s <- s1 + s, () + } + member x.While(guard : unit -> bool, body : State<'s, unit>) = + { run = fun s -> + let mutable s = s + while guard() do + let s1, () = body.run s + s <- s1 + s, () + } + let state = State.StateBuilder() module List = @@ -82,15 +112,55 @@ module Pickler = } let useType (t : FSharpType) = { run = fun s -> - let res = s.types |> List.tryPick (fun (v,i) -> if v = t then Some i else None) + let res = s.types |> List.tryPick (function (Choice1Of2 v,i) when v = t -> Some i | _ -> None) + match res with + | Some res -> + s, res + | None -> + let id = s.typeId + { s with typeId = id + 1; types = (Choice1Of2 t, id) :: s.types }, id + } + let useTypeDef (t : FSharpEntity) (targs : list) = + { run = fun s -> + let res = s.types |> List.tryPick (function (Choice2Of2(v,ta),i) when v = t && ta = targs -> Some i | _ -> None) match res with | Some res -> s, res | None -> let id = s.typeId - { s with typeId = id + 1; types = (t, id) :: s.types }, id + { s with typeId = id + 1; types = (Choice2Of2(t, targs), id) :: s.types }, id + } + let useMember (mem : FSharpMemberOrFunctionOrValue) (targs : list) (margs : list) = + { run = fun s -> + let res = s.members |> List.tryPick (function (Member(v,t,m),i) when v = mem && t = targs && m = margs -> Some i | _ -> None) + match res with + | Some res -> + s, res + | None -> + let id = s.memberId + { s with memberId = id + 1; members = (Member (mem, targs, margs), id) :: s.members }, id + } + let useUnionCase (case : FSharpUnionCase) (targs : list) = + { run = fun s -> + let res = s.members |> List.tryPick (function (UnionCase(c,t),i) when c = case && t = targs -> Some i | _ -> None) + match res with + | Some res -> + s, res + | None -> + let id = s.memberId + { s with memberId = id + 1; members = (UnionCase(case,targs), id) :: s.members }, id } + let useLiteral (v : obj) (t : FSharpType) = + { run = fun s -> + let res = s.literals |> List.tryPick (fun (vi,ti,i) -> if vi = v && ti = t then Some i else None) + match res with + | Some res -> + s, res + | None -> + let id = s.literalId + { s with literalId = id + 1; literals = (v, t, id) :: s.literals }, id + } type Writes private() = static member Write(s : BinaryWriter, v : byte) = s.Write v static member Write(s : BinaryWriter, v : byte[]) = s.Write v @@ -108,6 +178,9 @@ module Pickler = s.Write vs.Length for v in vs do s.Write v + static member Write(s : BinaryWriter, v : string[]) = + Writes.Write(s, v.Length) + for str in v do Writes.Write(s, str) let inline private writeAux< ^a, ^b, ^c when (^a or ^b or ^c) : (static member Write : ^a * ^b -> unit)> (c : ^c) (a : ^a) (b : ^b) = ((^a or ^b or ^c) : (static member Write : ^a * ^b -> unit) (a,b)) @@ -117,23 +190,25 @@ module Pickler = { run = fun s -> doit s.writer b s, () - } - -let bytes (v : int) = - System.BitConverter.GetBytes(v) -let mk (k : byte) (sub : byte[][]) = - [| - yield k - yield! System.BitConverter.GetBytes(sub.Length) - for s in sub do - yield! System.BitConverter.GetBytes(s.Length) - yield! s - |] - -// 1uy -> Lambda(var, body) -// 2uy -> Var(var) -// 3uy -> Closure(id) -// 4uy -> Let(var, e, b) + } + + + let pushCases (cs : array * FSharpExpr>) = + State.modify (fun s -> { s with cases = cs :: s.cases }) + + let popCases = + State.modify (fun s -> + match s.cases with + | _ :: cs -> { s with cases = cs } + | _ -> s + ) + + let getCase (i : int) = + State.get |> State.map (fun s -> + match s.cases with + | h :: _ -> h.[i] + | _ -> failwith "invalid case" + ) let rec serializeS (expr : FSharpExpr) = state { @@ -162,31 +237,292 @@ let rec serializeS (expr : FSharpExpr) = do! serializeS e do! serializeS b - // | BasicPatterns.Call(target, m, targs, margs, args) -> - // let decl = m.DeclaringEntity |> Option.get - - // let! tids = targs |> List.mapS Pickler.useType - // let! mids = margs |> List.mapS Pickler.useType + | BasicPatterns.Call(target, m, targs, margs, args) -> + //let! mem = Pickler.useMember m targs margs + let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + let! rid = Pickler.useType m.ReturnParameter.Type + let! margs = margs |> List.mapS Pickler.useType + let mpars = m.GenericParameters |> Seq.map (fun p -> p.Name) |> Seq.toArray + let! aids = m.CurriedParameterGroups |> Seq.concat |> Seq.toList |> List.mapS (fun p -> Pickler.useType p.Type) + match target with + | Some target -> + do! Pickler.write 5uy + do! Pickler.write tid + do! Pickler.write m.CompiledName + do! Pickler.write mpars + do! Pickler.write margs + do! Pickler.write aids + do! Pickler.write rid + do! Pickler.write args.Length + + do! serializeS target + for a in args do + do! serializeS a + + | _ -> + do! Pickler.write 6uy + do! Pickler.write tid + do! Pickler.write m.CompiledName + do! Pickler.write mpars + do! Pickler.write margs + do! Pickler.write aids + do! Pickler.write rid + do! Pickler.write args.Length + + for a in args do + do! serializeS a + + | BasicPatterns.AddressOf e -> + do! Pickler.write 7uy + do! serializeS e - // match target with - // | Some target -> - // do! Pickler.write 5uy - // do! Pickler.write (decl.TryGetFullCompiledName().Value) - // do! Pickler.write tids - // do! Pickler.write m.CompiledName - // do! Pickler.write mids - - // do! serializeS target + | BasicPatterns.AddressSet(v, e) -> + do! Pickler.write 8uy + do! serializeS v + do! serializeS e + + | BasicPatterns.AnonRecordGet(e, t, i) -> + let fieldName = t.AnonRecordTypeDetails.SortedFieldNames.[i] + let! typ = Pickler.useType t + do! Pickler.write 9uy + do! Pickler.write typ + do! Pickler.write fieldName + do! serializeS e - - // return () + | BasicPatterns.Application(e, ts, args) -> + do! Pickler.write 10uy + do! serializeS e + do! Pickler.write args.Length + for a in args do + do! serializeS a + + | BasicPatterns.Const(o, t) -> + do! Pickler.write 11uy + let! vid = Pickler.useLiteral o t + do! Pickler.write vid + + | BasicPatterns.IfThenElse(c, i, e) -> + do! Pickler.write 12uy + do! serializeS c + do! serializeS i + do! serializeS e - // | None -> + | BasicPatterns.UnionCaseTest(expr, typ, case) -> + do! Pickler.write 13uy + let! tid = Pickler.useType typ + do! Pickler.write tid + do! Pickler.write case.CompiledName + do! serializeS expr + + | BasicPatterns.UnionCaseGet(target, typ, case, prop) -> + let index = case.UnionCaseFields |> Seq.findIndex (fun pi -> pi = prop) + let! tid = Pickler.useType typ + + do! Pickler.write 14uy + do! Pickler.write tid + do! Pickler.write case.CompiledName + do! Pickler.write index + do! serializeS target + + | BasicPatterns.Coerce(t, e) -> + let! tid = Pickler.useType t + do! Pickler.write 15uy + do! Pickler.write tid + do! serializeS e + + | BasicPatterns.DefaultValue t -> + let! tid = Pickler.useType t + do! Pickler.write 16uy + do! Pickler.write tid + + | BasicPatterns.FastIntegerForLoop(s, e, BasicPatterns.Lambda(v, b), true) -> + let! vid = Pickler.newVar v + do! Pickler.write 17uy + do! Pickler.write vid + do! serializeS s + do! serializeS e + do! serializeS b + + | BasicPatterns.FSharpFieldGet(target, typ, field) -> + let! tid = Pickler.useType typ + let! ret = Pickler.useType field.FieldType + match target with + | Some target -> + do! Pickler.write 18uy + do! Pickler.write tid + do! Pickler.write field.Name + do! Pickler.write ret + do! serializeS target + | None -> + do! Pickler.write 19uy + do! Pickler.write tid + do! Pickler.write field.Name + do! Pickler.write ret + + | BasicPatterns.FSharpFieldSet(target, typ, field, value) -> + let! tid = Pickler.useType typ + let! ret = Pickler.useType field.FieldType + match target with + | Some target -> + do! Pickler.write 20uy + do! Pickler.write tid + do! Pickler.write field.Name + do! Pickler.write ret + do! serializeS target + do! serializeS value + | None -> + do! Pickler.write 21uy + do! Pickler.write tid + do! Pickler.write field.Name + do! Pickler.write ret + do! serializeS value + + | BasicPatterns.ILFieldGet(target, typ, field) -> + let! tid = Pickler.useType typ + let! ret = Pickler.useType expr.Type + match target with + | Some target -> + do! Pickler.write 18uy + do! Pickler.write tid + do! Pickler.write field + do! Pickler.write ret + do! serializeS target + | None -> + do! Pickler.write 19uy + do! Pickler.write tid + do! Pickler.write field + do! Pickler.write ret + + | BasicPatterns.ILFieldSet(target, typ, field, value) -> + let! tid = Pickler.useType typ + let! ret = Pickler.useType value.Type + match target with + | Some target -> + do! Pickler.write 20uy + do! Pickler.write tid + do! Pickler.write field + do! Pickler.write ret + do! serializeS target + do! serializeS value + | None -> + do! Pickler.write 21uy + do! Pickler.write tid + do! Pickler.write field + do! Pickler.write ret + do! serializeS value + + | BasicPatterns.LetRec(vs, b) -> + do! Pickler.write 22uy + do! Pickler.write vs.Length + for (v, e) in vs do + let! vid = Pickler.newVar v + do! Pickler.write vid + do! serializeS e + do! serializeS b + + | BasicPatterns.NewAnonRecord(typ, fields) -> + // code 23 + return failwith "bad" + + | BasicPatterns.NewArray(elementType, args) -> + let! tid = Pickler.useType elementType + do! Pickler.write 24uy + do! Pickler.write tid + do! Pickler.write args.Length + for a in args do do! serializeS a + + | BasicPatterns.NewDelegate _ -> + // code 25 + return failwith "bad" + + | BasicPatterns.NewObject(ctor, targs, args) -> + let! tid = Pickler.useTypeDef ctor.DeclaringEntity.Value targs + let! tids = args |> List.mapS (fun a -> Pickler.useType a.Type) + do! Pickler.write 26uy + do! Pickler.write tid + do! Pickler.write tids + for a in args do do! serializeS a + + | BasicPatterns.NewRecord(typ, args) -> + let! tid = Pickler.useType typ + do! Pickler.write 27uy + do! Pickler.write tid + do! Pickler.write args.Length + for a in args do do! serializeS a + + | BasicPatterns.NewTuple(typ, args) -> + do! Pickler.write 28uy + do! Pickler.write args.Length + for a in args do do! serializeS a + + | BasicPatterns.NewUnionCase(typ, case, args) -> + let! tid = Pickler.useType typ + do! Pickler.write 29uy + do! Pickler.write tid + do! Pickler.write case.Name + do! Pickler.write args.Length + for a in args do do! serializeS a + | BasicPatterns.Quote(e) -> + do! Pickler.write 30uy + do! serializeS e - // return () + | BasicPatterns.Sequential(l, r) -> + do! Pickler.write 31uy + do! serializeS l + do! serializeS r + | BasicPatterns.TupleGet(_typ, i, target) -> + do! Pickler.write 32uy + do! Pickler.write i + do! serializeS target + | BasicPatterns.TypeTest(typ, target) -> + let! tid = Pickler.useType typ + do! Pickler.write 33uy + do! Pickler.write tid + do! serializeS target + + | BasicPatterns.UnionCaseTag(e, t) -> + // code 34 + return failwith "bad" + | BasicPatterns.UnionCaseSet(target, typ, case, prop, value) -> + // code 35 + return failwith "bad" + | BasicPatterns.ValueSet(v, value) -> + let! var = Pickler.tryGetVar v + match var with + | Some var -> + do! Pickler.write 36uy + do! Pickler.write var + do! serializeS value + | None -> + // code 37 + return failwith "bad" + | BasicPatterns.WhileLoop(guard, body) -> + do! Pickler.write 38uy + do! serializeS guard + do! serializeS body + + | BasicPatterns.DecisionTreeSuccess(id, values) -> + let! (vars, body) = Pickler.getCase id + let bindings = List.zip vars values + let rec wrap (l : list) = + state { + match l with + | [] -> return! serializeS body + | (v,e) :: ls -> + let! var = Pickler.newVar v + do! Pickler.write 4uy + do! Pickler.write var + do! serializeS e + do! wrap ls + } + do! wrap bindings + + | BasicPatterns.DecisionTree(target, cases) -> + do! Pickler.pushCases (List.toArray cases) + do! serializeS target + do! Pickler.popCases | _ -> - let code = sprintf "BAD EXPRESSION: %A" expr do! Pickler.write 255uy do! Pickler.write (sprintf "BAD EXPRESSION: %A" expr) } @@ -196,17 +532,20 @@ type VarData = type ExprData = { - typ : FSharpType - variables: VarData[] - values : FSharpMemberOrFunctionOrValue[] - data : byte[] + typ : FSharpType + variables : VarData[] + values : FSharpMemberOrFunctionOrValue[] + members : array + types : Choice>[] + literals : array + data : byte[] } let serialize (expr : FSharpExpr) = let s = serializeS expr use stream = new System.IO.MemoryStream() use w = new System.IO.BinaryWriter(stream) - let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = [] } + let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = [] } w.Flush() let data = stream.ToArray() let variables = @@ -216,15 +555,17 @@ let serialize (expr : FSharpExpr) = { name = m.DisplayName; typ = m.FullType; isMutable = m.IsMutable } ) |> List.toArray - let values = - s.values - |> List.sortBy snd - |> List.map fst - |> List.toArray + let values = s.values |> List.sortBy snd |> List.map fst |> List.toArray + let types = s.types |> List.sortBy snd |> List.map fst |> List.toArray + let members = s.members |> List.sortBy (fun (_,i) -> i) |> List.map (fun (m,_) -> m) |> List.toArray + let literals = s.literals |> List.sortBy (fun (_,_,i) -> i) |> List.map (fun (t, v, _) -> t, v) |> List.toArray { typ = expr.Type variables = variables values = values + types = types + members = members + literals = literals data = data } diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index 43fe407a4c..ab6a26bc23 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -1,8 +1,10 @@ module ExprUtils open Microsoft.FSharp.Quotations +open Fable.Core open Fable.Import.JS - +open System.Reflection +open FSharp.Collections type IValue = abstract member typ : System.Type @@ -14,19 +16,35 @@ type IVariable = abstract member name : string abstract member isMutable : bool +type ILiteral = + abstract member typ : System.Type + abstract member value : obj + + type BinaryStream(arr : Uint8Array) = let view = DataView.Create(arr.buffer, arr.byteOffset, arr.byteLength) let mutable position = 0 + member x.Position = position + member x.ReadByte() = - let value = view.getUint8(float position) + let value = arr.[position] //view.getUint8(float position) position <- position + 1 - byte value + unbox value member x.ReadInt32() = let value = view.getInt32(float position, true) position <- position + 4 - int value + unbox value + + member x.ReadInt32Array() = + let length = x.ReadInt32() + FSharp.Collections.Array.init length (fun _ -> x.ReadInt32()) + + member x.ReadStringArray() = + let length = x.ReadInt32() + FSharp.Collections.Array.init length (fun _ -> x.ReadString()) + member x.ReadString() = let length = x.ReadInt32() @@ -40,14 +58,44 @@ type BinaryStream(arr : Uint8Array) = // 2uy -> Var(var) // 3uy -> Closure(id) // 4uy -> Let(var, e, b) +[ t[$2]), ((t, v) => { t[$2] = v; }))")>] +let createRecordProperty (decl : System.Type) (name : string) (typ : System.Type) : PropertyInfo = jsNative +[] +let createStaticProperty (decl : System.Type) (name : string) (typ : System.Type) : PropertyInfo = jsNative + + +// declaringType: NTypeInfo, +// genericArguments: NTypeInfo[], +// name: string, +// parameters: NParameterInfo[], +// returnType: NTypeInfo, +// isStatic: boolean, +// private invoke: (...args: any[]) => any, +// attributes: CustomAttribute[], +// private declaration?: NMethodInfo, + +[] +let createMethod (decl : System.Type) (name : string) (mpars : string[]) (margs : System.Type[]) (declaredArgs : System.Type[]) (ret : System.Type) (isStatic : bool) : MethodInfo = jsNative + + -let deserialize (values : IValue[]) (variables : IVariable[]) (data : string) : Expr = +let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Type[]) (_members : System.Reflection.MemberInfo[]) (literals : ILiteral[]) (data : string) : Expr = let arr = System.Convert.FromBase64String(data) let stream = BinaryStream(unbox arr) let values = values |> FSharp.Collections.Array.map (fun v -> Expr.ValueWithName(v.value, v.typ, v.name)) let variables = variables |> FSharp.Collections.Array.map (fun v -> Var(v.name, v.typ, v.isMutable)) + let init (n : int) (f : int -> 'a) = + let rec init (i : int) = + if i >= n then + [] + else + let h = f i + h :: init (i + 1) + init 0 + + let rec read () = let tag = stream.ReadByte() match tag with @@ -66,11 +114,274 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (data : string) : let e = read() let b = read() Expr.Let(variables.[vid], e, b) + + | 5uy -> + let decl = types.[stream.ReadInt32()] + let name = stream.ReadString() + let mpars = stream.ReadStringArray() + let margs = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) + let dargs = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) + let ret = types.[stream.ReadInt32()] + let cnt = stream.ReadInt32() + + let target = read() + let args = init cnt (fun _ -> read()) + + let mem = + decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> + m.Name = name && m.GetParameters().Length = cnt && + m.GetGenericArguments().Length = margs.Length && + FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) + (if m.IsGenericMethod then m.MakeGenericMethod(margs).GetParameters() else m.GetParameters()) + (List.toArray args) + ) + + match mem with + | Some mem -> + let mem = + if margs.Length > 0 then mem.MakeGenericMethod margs + else mem + Expr.Call(target, mem, args) + | None -> + let mem = createMethod decl name mpars margs dargs ret false + Expr.Call(target, mem, args) + + | 6uy -> + let decl = types.[stream.ReadInt32()] + let name = stream.ReadString() + let mpars = stream.ReadStringArray() + let margs = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) + let dargs = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) + let ret = types.[stream.ReadInt32()] + let cnt = stream.ReadInt32() + + let args = init cnt (fun _ -> read()) + + let mem = + decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> + m.Name = name && m.GetParameters().Length = cnt && + m.GetGenericArguments().Length = margs.Length && + FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) + (if m.IsGenericMethod then m.MakeGenericMethod(margs).GetParameters() else m.GetParameters()) + (List.toArray args) + ) + + match mem with + | Some mem -> + let mem = + if margs.Length > 0 then mem.MakeGenericMethod margs + else mem + Expr.Call(mem, args) + | None -> + let mem = createMethod decl name mpars margs dargs ret true + Expr.Call(mem, args) + + | 7uy -> + let e = read() + Expr.AddressOf(e) + + | 8uy -> + let v = read() + let e = read() + Expr.AddressSet(v, e) + + | 9uy -> + let tid = stream.ReadInt32() + let name = stream.ReadString() + let target = read() + //let prop = FSharp.Reflection.FSharpType.GetRecordFields target.Type |> FSharp.Collections.Array.find (fun p -> p.Name = name) + let prop = createRecordProperty target.Type name types.[tid] + Expr.PropertyGet(target, prop) + + | 10uy -> + let f = read() + let cnt = stream.ReadInt32() + let args = init cnt (fun _ -> read()) + Expr.Applications(f, List.map List.singleton args) + | 11uy -> + let id = stream.ReadInt32() + let l = literals.[id] + Expr.Value(l.value, l.typ) + | 12uy -> + let c = read() + let i = read() + let e = read() + Expr.IfThenElse(c, i, e) + + | 13uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let e = read() + let case = FSharp.Reflection.FSharpType.GetUnionCases(e.Type) |> FSharp.Collections.Array.find (fun c -> c.Name = name) + Expr.UnionCaseTest(e, case) + + | 14uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let index = stream.ReadInt32() + let target = read() + let case = FSharp.Reflection.FSharpType.GetUnionCases(typ) |> FSharp.Collections.Array.find (fun c -> c.Name = name) + let prop = case.GetFields().[index] + + Expr.PropertyGet(target, prop) + | 15uy -> + let typ = types.[stream.ReadInt32()] + let e = read() + Expr.Coerce(e, typ) + + | 16uy -> + let typ = types.[stream.ReadInt32()] + Expr.DefaultValue typ + + | 17uy -> + let var = variables.[stream.ReadInt32()] + let s = read() + let e = read() + let b = read() + Expr.ForIntegerRangeLoop(var, s, e, b) + + | 18uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let ret = types.[stream.ReadInt32()] + let target = read() + + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) + match prop with + | Some prop -> + Expr.PropertyGet(target, prop) + | None -> + let prop = createRecordProperty typ name ret + Expr.PropertyGet(target, prop) + + | 19uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let ret = types.[stream.ReadInt32()] + + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) + match prop with + | Some prop -> + Expr.PropertyGet(prop) + | None -> + let prop = createStaticProperty typ name ret + Expr.PropertyGet(prop) + + | 20uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let ret = types.[stream.ReadInt32()] + let target = read() + let value = read() + + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) + match prop with + | Some prop -> + Expr.PropertySet(target, prop, value) + | None -> + let prop = createRecordProperty typ name ret + Expr.PropertySet(target, prop, value) + + | 21uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let ret = types.[stream.ReadInt32()] + let value = read() + + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) + match prop with + | Some prop -> + Expr.PropertySet(prop, value) + | None -> + let prop = createStaticProperty typ name ret + Expr.PropertySet(prop, value) + + | 22uy -> + let cnt = stream.ReadInt32() + let bindings = + init cnt (fun _ -> + let v = variables.[stream.ReadInt32()] + let e = read() + v, e + ) + let body = read() + Expr.LetRecursive(bindings, body) + + | 24uy -> + let typ = types.[stream.ReadInt32()] + let cnt = stream.ReadInt32() + let args = init cnt (fun _ -> read()) + Expr.NewArray(typ, args) + + | 26uy -> + let typ = types.[stream.ReadInt32()] + let argts = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) + let args = init argts.Length (fun _ -> read()) + + let ctor = + typ.GetConstructors() + |> FSharp.Collections.Array.tryFind (fun ctor -> + ctor.GetParameters().Length = argts.Length && + FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) t -> p.ParameterType = t) (ctor.GetParameters()) argts + ) + + match ctor with + | Some ctor -> + Expr.NewObject(ctor, args) + | _ -> + failwith "no ctor found" + + | 27uy -> + let typ = types.[stream.ReadInt32()] + let cnt = stream.ReadInt32() + let args = init cnt (fun _ -> read()) + Expr.NewRecord(typ, args) + + | 28uy -> + let cnt = stream.ReadInt32() + let args = init cnt (fun _ -> read()) + Expr.NewTuple(args) + + | 29uy -> + let typ = types.[stream.ReadInt32()] + let name = stream.ReadString() + let cnt = stream.ReadInt32() + let args = init cnt (fun _ -> read()) + // TODO: non existing unions + let case = FSharp.Reflection.FSharpType.GetUnionCases(typ) |> FSharp.Collections.Array.find (fun c -> c.Name = name) + Expr.NewUnionCase(case, args) + | 30uy -> + let e = read() + Expr.Quote(e) + + | 31uy -> + let l = read() + let r = read() + Expr.Sequential(l, r) + + | 32uy -> + let i = stream.ReadInt32() + let t = read() + Expr.TupleGet(t, i) + + | 33uy -> + let typ = types.[stream.ReadInt32()] + let target = read() + Expr.TypeTest(target, typ) + | 36uy -> + let v = variables.[stream.ReadInt32()] + let value = read() + Expr.VarSet(v, value) + | 38uy -> + let guard = read() + let body = read() + Expr.WhileLoop(guard, body) + | 255uy -> let str = stream.ReadString() failwithf "unsupported expression: %s" str | _ -> - failwith "invalid expression" + failwithf "invalid expression: %A at %A" tag stream.Position read() diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 296f67e652..24f47ce624 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -574,61 +574,65 @@ module Patterns = /// Returns type of lambda application - something like "(fun a -> ..) b" let rec typeOfAppliedLambda f = - let fty = ((typeOf f):Type) + let fty = ((exprType 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 - | 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 - | InstancePropSetOp _, _ -> typeof - | InstanceFieldGetOp fld, _ -> fld.FieldType - | StaticFieldGetOp fld, _ -> fld.FieldType - | InstanceFieldSetOp _, _ -> typeof - | StaticFieldSetOp _, _ -> typeof - | NewObjectOp ctor, _ -> ctor.DeclaringType - | InstanceMethodCallOp minfo, _ -> minfo.ReturnType |> removeVoid - | StaticMethodCallOp minfo, _ -> minfo.ReturnType |> removeVoid - | CoerceOp ty, _ -> ty - | SequentialOp, [_;b] -> typeOf b - | ForIntegerRangeLoopOp, _ -> typeof - | NewArrayOp ty, _ -> mkArrayTy ty - | NewDelegateOp ty, _ -> ty - | DefaultValueOp ty, _ -> ty - | TypeTestOp _, _ -> typeof - | QuoteOp true, [expr] -> mkExprTy (typeOf expr) - | QuoteOp false, [_] -> rawExprTy - | TryFinallyOp, [e1;_] -> typeOf e1 - | TryWithOp, [e1;_;_] -> typeOf e1 - | WhileLoopOp, _ - | VarSetOp, _ - | AddressSetOp, _ -> typeof - | AddressOfOp, [expr]-> failwith "(typeOf expr).MakeByRefType()" - | (AddressOfOp | QuoteOp _ | SequentialOp | TryWithOp | TryFinallyOp | IfThenElseOp | AppOp), _ -> failwith "unreachable" - + and exprType (e : Expr) : Type = + let res = + let (E t) = e + match t with + | VarTerm v -> v.Type + | LambdaTerm (v, b) -> mkFunTy v.Type (exprType b) + | HoleTerm (ty, _) -> ty + | CombTerm (c, args) -> + match c, args with + | AppOp, [f;_] -> typeOfAppliedLambda f + | LetOp, _ -> match e with Let(_, _, b) -> exprType b | _ -> failwith "unreachable" + | IfThenElseOp, [_;t;_] -> exprType t + | LetRecOp, _ -> match e with LetRecursive(_, b) -> exprType b | _ -> failwith "unreachable" + | LetRecCombOp, _ -> failwith "typeOfConst: LetRecCombOp" + | NewRecordOp ty, _ -> ty + | NewUnionCaseOp unionCase, _ -> unionCase.DeclaringType + | UnionCaseTestOp _, _ -> typeof + | 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 + | InstancePropSetOp _, _ -> typeof + | InstanceFieldGetOp fld, _ -> fld.FieldType + | StaticFieldGetOp fld, _ -> fld.FieldType + | InstanceFieldSetOp _, _ -> typeof + | StaticFieldSetOp _, _ -> typeof + | NewObjectOp ctor, _ -> ctor.DeclaringType + | InstanceMethodCallOp minfo, _ -> minfo.ReturnType + | StaticMethodCallOp minfo, _ -> minfo.ReturnType + | CoerceOp ty, _ -> ty + | SequentialOp, [_;b] -> exprType b + | ForIntegerRangeLoopOp, _ -> typeof + | NewArrayOp ty, _ -> mkArrayTy ty + | NewDelegateOp ty, _ -> ty + | DefaultValueOp ty, _ -> ty + | TypeTestOp _, _ -> typeof + | QuoteOp true, [expr] -> mkExprTy (exprType expr) + | QuoteOp false, [_] -> rawExprTy + | TryFinallyOp, [e1;_] -> exprType e1 + | TryWithOp, [e1;_;_] -> exprType e1 + | WhileLoopOp, _ + | VarSetOp, _ + | AddressSetOp, _ -> typeof + | AddressOfOp, [expr]-> failwith "(typeOf expr).MakeByRefType()" + | (AddressOfOp | QuoteOp _ | SequentialOp | TryWithOp | TryFinallyOp | IfThenElseOp | AppOp), _ -> failwith "unreachable" + if unbox res then + res + else + failwithf "bad type for: %A" e //-------------------------------------------------------------------------- // Constructors for building Raw quotations @@ -651,7 +655,7 @@ module Patterns = let checkTypesSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) = if (expectedType <> receivedType) then - invalidArg "receivedType" (String.Format(threeHoleSR, name, expectedType, receivedType)) + invalidArg "receivedType" (String.Format("{0}/{1}: {1} {2}", threeHoleSR, name, expectedType.FullName, receivedType.FullName)) let checkTypesWeakSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) = () @@ -661,7 +665,7 @@ module Patterns = let checkArgs (paramInfos: ParameterInfo[]) (args:list) = 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)")) + ( fun (p:ParameterInfo) a -> checkTypesWeakSR p.ParameterType (exprType a) "args" ("SR.GetString(SR.QtmmInvalidParam)")) (paramInfos |> Array.toList) args // todo: shouldn't this be "strong" type check? sometimes? @@ -679,10 +683,10 @@ module Patterns = // Checks lambda application for correctness let checkAppliedLambda (f, v) = - let fty = typeOf f + let fty = exprType f let ftyG = (if fty.IsGenericType then fty.GetGenericTypeDefinition() else fty) checkTypesSR funTyC ftyG "f" ("SR.GetString(SR.QtmmExpectedFunction)") - let vty = (typeOf v) + let vty = (exprType v) match fty.GetGenericArguments() with | [| a; _ |] -> checkTypesSR vty a "f" ("SR.GetString(SR.QtmmFunctionArgTypeMismatch)") | _ -> invalidArg "f" ("SR.GetString(SR.QinvalidFuncType)") @@ -695,7 +699,7 @@ module Patterns = | _ -> invalidArg "ty" ("String.Format(SR.GetString(SR.notAUnionType), ty.FullName)") let checkBind(v:Var, e) = - let ety = typeOf e + let ety = exprType e checkTypesSR v.Type ety "let" ("SR.GetString(SR.QtmmVarTypeNotMatchRHS)") // [Correct by definition] @@ -735,15 +739,15 @@ module Patterns = 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 + List.iter2(fun mt a -> checkTypesSR mt (exprType 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)) + let ty = FSharpType.MakeTupleType(Array.map exprType (Array.ofList args)) mkFEN (NewTupleOp ty) args let mkTupleGet (ty, n, x) = - checkTypesSR ty (typeOf x) "tupleGet" ("SR.GetString(SR.QtmmExprNotMatchTuple)") + checkTypesSR ty (exprType 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 @@ -752,7 +756,7 @@ module Patterns = let mkNewRecord (ty, args:list) = let mems = FSharpType.GetRecordFields(ty) 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 + List.iter2 (fun (minfo:PropertyInfo) a -> checkTypesSR minfo.PropertyType (exprType a) "recd" ("SR.GetString(SR.QtmmIncorrectArgForRecord)")) (Array.toList mems) args mkFEN (NewRecordOp ty) args @@ -761,22 +765,22 @@ module Patterns = if Unchecked.defaultof = unionCase then raise (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 + List.iter2 (fun (minfo:PropertyInfo) a -> checkTypesSR minfo.PropertyType (exprType a) "sum" ("SR.GetString(SR.QtmmIncorrectArgForUnion)")) (Array.toList sargs) args mkFEN (NewUnionCaseOp unionCase) args let mkUnionCaseTest (unionCase:UnionCaseInfo, expr) = if Unchecked.defaultof = unionCase then raise (ArgumentNullException()) - checkTypesSR unionCase.DeclaringType (typeOf expr) "UnionCaseTagTest" ("SR.GetString(SR.QtmmExprTypeMismatch)") + checkTypesSR unionCase.DeclaringType (exprType 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) (typeOf e) "cond" ("SR.GetString(SR.QtmmCondMustBeBool)") + checkTypesSR (exprType t) (exprType f) "cond" ("SR.GetString(SR.QtmmTrueAndFalseMustMatch)") + checkTypesSR (typeof) (exprType 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 + List.iter (fun a -> checkTypesSR ty (exprType a) "newArray" ("SR.GetString(SR.QtmmInitArray)")) args mkFEN (NewArrayOp ty) args let mkInstanceFieldGet(obj, finfo:FieldInfo) = @@ -795,14 +799,14 @@ module Patterns = let mkStaticFieldSet (finfo:FieldInfo, value:Expr) = if Unchecked.defaultof = finfo then raise (ArgumentNullException()) - checkTypesSR (typeOf value) finfo.FieldType "value" ("SR.GetString(SR.QtmmBadFieldType)") + //checkTypesSR (exprType 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 = finfo then raise (ArgumentNullException()) - checkTypesSR (typeOf value) finfo.FieldType "value" ("SR.GetString(SR.QtmmBadFieldType)") + //checkTypesSR (exprType value) finfo.FieldType "value" ("SR.GetString(SR.QtmmBadFieldType)") match finfo.IsStatic with | false -> checkObj finfo obj @@ -819,39 +823,43 @@ module Patterns = let mkStaticPropGet (pinfo:PropertyInfo, args:list) = if Unchecked.defaultof = pinfo then raise (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)") + mkFEN (StaticPropGetOp pinfo) args + // 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) = if Unchecked.defaultof = pinfo then raise (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)") + mkFEN (InstancePropGetOp pinfo) (obj::args) + // 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, value:Expr) = if Unchecked.defaultof = pinfo then raise (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)") + mkFEN (StaticPropSetOp pinfo) (args@[value]) + // 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, value:Expr) = if Unchecked.defaultof = pinfo then raise (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)") + mkFEN (InstancePropSetOp pinfo) (obj::(args@[value])) + // 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) = if Unchecked.defaultof = minfo then raise (ArgumentNullException()) @@ -870,14 +878,14 @@ module Patterns = | false -> invalidArg "minfo" ("SR.GetString(SR.QnonStaticNoReceiverObject)") let mkForLoop (v:Var, lowerBound, upperBound, body) = - checkTypesSR (typeof) (typeOf lowerBound) "lowerBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") - checkTypesSR (typeof) (typeOf upperBound) "upperBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") + checkTypesSR (typeof) (exprType lowerBound) "lowerBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") + checkTypesSR (typeof) (exprType upperBound) "upperBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") checkTypesSR (typeof) (v.Type) "for" ("SR.GetString(SR.QtmmLoopBodyMustBeLambdaTakingInteger)") mkFE3 ForIntegerRangeLoopOp (lowerBound, upperBound, mkLambda(v, body)) let mkWhileLoop (guard, body) = - checkTypesSR (typeof) (typeOf guard) "guard" ("SR.GetString(SR.QtmmGuardMustBeBool)") - checkTypesSR (typeof) (typeOf body) "body" ("SR.GetString(SR.QtmmBodyMustBeUnit)") + checkTypesSR (typeof) (exprType guard) "guard" ("SR.GetString(SR.QtmmGuardMustBeBool)") + checkTypesSR (typeof) (exprType body) "body" ("SR.GetString(SR.QtmmBodyMustBeUnit)") mkFE2 (WhileLoopOp) (guard, body) let mkNewDelegate (ty, e) = @@ -886,7 +894,7 @@ module Patterns = let ps = targs |> Array.take nargs let ret = targs.[targs.Length - 1] let dlfun = Array.foldBack mkFunTy ps ret - checkTypesSR dlfun (typeOf e) "ty" ("SR.GetString(SR.QtmmFunTypeNotMatchDelegate)") + checkTypesSR dlfun (exprType e) "ty" ("SR.GetString(SR.QtmmFunTypeNotMatchDelegate)") mkFE1 (NewDelegateOp ty) e let mkLet (v, e, b) = @@ -1416,7 +1424,7 @@ module Patterns = match a with | Unique v -> v | Ambiguous f -> - let argTys = List.map typeOf args + let argTys = List.map exprType args f argTys let tyargs = b env.typeInst E (CombTerm (a tyargs, args))) @@ -1596,7 +1604,7 @@ module Patterns = | HoleTerm (ty, idx) -> if idx < 0 || idx >= l.Length then failwith "hole index out of range" let h = l.[idx] - match typeOf h with + match exprType h with | expected when expected <> ty -> invalidArg "receivedType" ("String.Format(SR.GetString(SR.QtmmRaw), expected, ty)") | _ -> h @@ -1895,7 +1903,7 @@ module Patterns = let cast (expr: Expr) : Expr<'T> = - checkTypesSR (typeof<'T>) (typeOf expr) "expr" ("SR.GetString(SR.QtmmExprHasWrongType)") + checkTypesSR (typeof<'T>) (exprType expr) "expr" ("SR.GetString(SR.QtmmExprHasWrongType)") new Expr<'T>(expr.Tree, expr.CustomAttributes) open Patterns @@ -1904,7 +1912,7 @@ open Patterns type Expr with member x.Substitute substitution = substituteRaw substitution x member x.GetFreeVars () = (freeInExpr x :> seq<_>) - member x.Type = typeOf x + member x.Type = exprType x static member AddressOf (target:Expr) = mkAddressOf target @@ -2015,7 +2023,7 @@ type Expr with mkTryFinally (body, compensation) static member TupleGet (tuple:Expr, index:int) = - mkTupleGet (typeOf tuple, index, tuple) + mkTupleGet (exprType tuple, index, tuple) static member TypeTest (source: Expr, target: Type) = checkNonNull "target" target diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index fcd5e920fe..35f48311c3 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -112,7 +112,7 @@ export class NMethodInfo extends NMethodBase { attributes: CustomAttribute[], private declaration?: NMethodInfo, ) { - super(declaringType, name, parameters, isStatic, attributes); + super(declaringType, name, parameters.map((a) => a), isStatic, attributes); if (!returnType) { throw new Error(`MethodInfo ${name} does not have a return type`); } @@ -130,11 +130,22 @@ export class NMethodInfo extends NMethodBase { } this.Parameters.forEach((p) => { p.ParameterType = this.ResolveGeneric(p.ParameterType); }); this.ReturnType = returnType.get_ContainsGenericParameters() ? this.ResolveGeneric(returnType) : returnType; + if (!this.ReturnType) { throw new Error(`MethodInfo ${name} does not have a return type`); } + } public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { - return t.fullname in this.genMap ? this.GenericArguments[this.genMap[t.fullname]] : this.DeclaringType.ResolveGeneric(t); + if (t.fullname in this.genMap) { + const idx = this.genMap[t.fullname]; + if (idx < 0 || idx > this.GenericArguments.length) { + throw new Error(`invalid generic index ${idx}`); + } + + return this.GenericArguments[idx]; + } else { + return this.DeclaringType.ResolveGeneric(t); + } } else if (t.get_ContainsGenericParameters()) { const gen = t.generics.map((t) => this.ResolveGeneric(t)); return t.MakeGenericType(gen); @@ -280,6 +291,8 @@ export class NPropertyInfo extends NMemberInfo { public IsStatic: boolean, public IsFSharp: boolean, attributes: CustomAttribute[], + private get?: (target: any) => any, + private set?: (target: any, value: any) => void, ) { super(DeclaringType, Name, attributes); @@ -292,10 +305,10 @@ export class NPropertyInfo extends NMemberInfo { public get_IsFSharp() { return this.IsFSharp; } public get_CanRead() { - return this.get_GetMethod() !== null; + return this.get || this.get_GetMethod(); } public get_CanWrite() { - return this.get_SetMethod() !== null; + return this.set || this.get_SetMethod(); } public GetIndexParameters() { @@ -318,7 +331,9 @@ export class NPropertyInfo extends NMemberInfo { } public GetValue(target: any, index: any[]): any { - if (this.IsFSharp) { + if (this.get) { + return this.get(target); + } else if (this.IsFSharp) { // TODO: mangled-names???? if (this.Name in target) { return target[this.Name]; @@ -334,7 +349,9 @@ export class NPropertyInfo extends NMemberInfo { } public SetValue(target: any, value: any, index: any[]) { - if (this.IsFSharp) { + if (this.set) { + return this.set(target, value); + } else if (this.IsFSharp) { target[this.Name] = value; } else { index = index || []; @@ -383,7 +400,7 @@ export class NPropertyInfo extends NMemberInfo { } export class NUnionCaseInfo extends NMemberInfo { - public Fields: Array<[string, NTypeInfo]> = null; + public Fields: NPropertyInfo[] = null; public constructor( DeclaringType: NTypeInfo, @@ -398,11 +415,16 @@ export class NUnionCaseInfo extends NMemberInfo { if (typeof Tag !== "number") { throw new Error(`UnionCase ${Name} does not have a tag`); } fields = fields || []; - this.Fields = fields.map ((tup) => tup[1].get_ContainsGenericParameters() ? [tup[0], DeclaringType.ResolveGeneric(tup[1])] : tup) as Array<[string, NTypeInfo]>; + this.Fields = fields.map ((tup, i) => { + const name = tup[0]; + const typ = tup[1].get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(tup[1]) : tup[1]; + return new NPropertyInfo(DeclaringType, name, typ, false, true, [], (t) => t.fields[i], (t, v) => { t.fields[i] = v; }); + // tup[1].get_ContainsGenericParameters() ? [tup[0], DeclaringType.ResolveGeneric(tup[1])] : tup) as Array<[string, NTypeInfo]>; + }); } public GetFields() { - return this.Fields.map((nt) => new NPropertyInfo(this.DeclaringType, nt[0], nt[1], false, true, [])); + return this.Fields; } public get_Tag() { @@ -411,7 +433,7 @@ export class NUnionCaseInfo extends NMemberInfo { public toPrettyString() { const decl = this.DeclaringType.toFullString(); - const fields = this.Fields.map((tup) => tup[0] + ": " + tup[1].toFullString()).join(" * "); + const fields = this.Fields.map((tup) => tup.Name + ": " + tup.Type.toFullString()).join(" * "); return decl + "." + this.Name + " of " + fields; } @@ -440,9 +462,9 @@ export class NTypeInfo { public generics: NTypeInfo[] = null; public GenericDeclaration: NTypeInfo = null; public DeclaringType: NTypeInfo = null; + public mems: NMemberInfo[] = null; private instantiations: {[i: string]: NTypeInfo} = {}; - private mems: NMemberInfo[] = null; private genericMap: {[name: string]: number} = {}; constructor( @@ -514,9 +536,9 @@ export class NTypeInfo { } public MakeGenericType(args: NTypeInfo[]) { - args = args.filter((a) => a); + // args = args.filter((a) => a); if (args.length === 0) { return this; } - if (args.length !== this.genericCount) { throw new Error(`${this.fullname} contains ${this.genericCount} generic parameters but only ${this.generics.length} given.`); } + if (args.length !== this.genericCount) { throw new Error(`${this.fullname} contains ${this.genericCount} generic parameters but only ${args.length} given.`); } const key = args.map((t) => t.toString()).join(", "); if (key in this.instantiations) { @@ -810,7 +832,7 @@ export function lambda(argType: NTypeInfo, returnType: NTypeInfo): NTypeInfo { export function option(generic?: NTypeInfo): NTypeInfo { const name = "Microsoft.FSharp.Core.FSharpOption`1"; const gen = - declareNType(name, 2, (self, gen) => [ + declareNType(name, 1, (self, gen) => [ new NUnionCaseInfo(self, 0, "None", [], [], ((_) => null)), new NUnionCaseInfo(self, 1, "Some", [], [["Value", gen[0]]], ((args, ...rest) => args)), ]); @@ -997,7 +1019,7 @@ export function getUnionFields(v: any, t: NTypeInfo): [NUnionCaseInfo, any[]] { } export function getUnionCaseFields(uci: NUnionCaseInfo): NPropertyInfo[] { - return uci.Fields.map((nt) => new NPropertyInfo(uci.DeclaringType, nt[0], nt[1], false, true, [])); + return uci.Fields; } export function getRecordFields(v: any): any[] { @@ -1060,3 +1082,21 @@ export function getCaseFields(x: any): any[] { assertUnion(x); return x.fields; } + +export function createMethod(decl: NTypeInfo, name: string, mpars: string[], margs: NTypeInfo[], declaredArgs: NTypeInfo[], ret: NTypeInfo, isStatic: boolean): NMethodInfo { + const found = + decl.GetMethods().find((m) => + m.Name === name && m.GenericArguments.length === margs.length && + m.Parameters.length === declaredArgs.length && m.IsStatic === isStatic && + (m.get_IsGenericMethod() ? m.MakeGenericMethod(margs).ParametersAssignable(declaredArgs) : m.ParametersAssignable(declaredArgs)) && + equals(m.ReturnType, ret) + ); + if (found) { + return found.get_IsGenericMethod() ? found.MakeGenericMethod(margs) : found; + } else { + const pp = mpars.map ((n) => getGenericParameter(n)); + const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => null), []); + decl.mems.push(meth); + return meth.get_IsGenericMethod() ? meth.MakeGenericMethod(margs) : meth; + } +} diff --git a/src/fable-library/String.ts b/src/fable-library/String.ts index ff9f632601..14b93bd69f 100644 --- a/src/fable-library/String.ts +++ b/src/fable-library/String.ts @@ -386,12 +386,52 @@ export function toBase64String(inArray: number[]) { return typeof btoa === "function" ? btoa(str) : notSupported("btoa"); } + + export function fromBase64String(b64Encoded: string) { - const binary = typeof atob === "function" ? atob(b64Encoded) : notSupported("atob"); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) { - bytes[i] = binary.charCodeAt(i); + function toValue(c: number) { + if (c >= 65 && c <= 90) { + return c - 65; + } else if (c >= 97 && c <= 122) { + return 26 + c - 97; + } else if (c >= 48 && c <= 57) { + return 52 + c - 48; + } else if (c === 43) { + return 62; + } else if (c === 47) { + return 63; + } else { + return 0; + } + } + let pad = 0; + while (b64Encoded.charAt(b64Encoded.length - 1 - pad) === "=") { + pad = pad + 1; + } + const length = 3 * b64Encoded.length / 4 - pad; + + const bytes = new Uint8Array(length); + + let o = 0; + for (let i = 0; i < bytes.length; i += 3) { + const c0 = toValue(b64Encoded.charCodeAt(o)); + const c1 = toValue(b64Encoded.charCodeAt(o + 1)); + const c2 = toValue(b64Encoded.charCodeAt(o + 2)); + const c3 = toValue(b64Encoded.charCodeAt(o + 3)); + + const int = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3; + + bytes[i + 0] = (int >> 16) & 0xFF; + if (i + 1 < bytes.length) { bytes[i + 1] = (int >> 8) & 0xFF; } + if (i + 2 < bytes.length) { bytes[i + 2] = (int) & 0xFF; } + o += 4; } + + // const binary = typeof atob === "function" ? atob(b64Encoded) : notSupported("atob"); + // const bytes = new Uint8Array(binary.length); + // for (let i = 0; i < binary.length; i++) { + // bytes[i] = binary.charCodeAt(i); + // } return bytes; } diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 9a17b96392..391ffa33fd 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -43,6 +43,8 @@ type V2 = { x : int; y : int } with member x.Sepp = x.x + x.y + static member GetX (v : V2) = v.x + static member (+) (l : V2, r : V2) = { x = l.x + r.x; y = l.y + r.y } member x.Item @@ -62,6 +64,12 @@ type Sepp(a : int, b : string) = member x.Yeah = b member x.DoIt(c : int) = a*c + +type MyUnion = + | Values of int * int + | Single of value : int + + let tests = testList "Expr" [ @@ -167,9 +175,71 @@ let tests = | _ -> failwith "bad record" - testCase "CustomAttributes" <| fun () -> - let b = 10 - let test = <@ fun (a : int) -> b @> + testCase "Quote static call library" <| fun () -> + + match <@@ fun (a : int) -> a + 1 @@> with + | Lambda(va,Call(None, add, [Var a; Value(x,y)])) -> + equal "op_Addition" add.Name + equal 3 (add.GetGenericArguments().Length) + equal a va + equal (1 :> obj) x + equal y typeof + | e -> + failwithf "bad expression: %A" e + + testCase "Quote static call user" <| fun () -> + + match <@@ V2.GetX @@> with + | Lambda(va,Call(None, m, [Var a])) -> + equal "GetX" m.Name + equal m (typeof.GetMethod("GetX")) + equal a va + | e -> + failwithf "bad expression: %A" e + + testCase "Quote option deconstruct" <| fun () -> + match <@@ fun (v : Option) -> match v with | Some a -> a | None -> 0 @@> with + | Lambda(va,IfThenElse(UnionCaseTest(Var v, c), a, b)) -> + equal va v + | e -> + failwithf "bad expression: %A" e + + testCase "Quote record property" <| fun () -> + match <@@ fun (v : V2) -> List.empty @@> with + | Lambda(va,PropertyGet(None, prop, [])) -> + failwithf "%A" prop + // equal va v + // equal prop (typeof.GetProperty("x")) + | e -> + failwithf "bad expression: %A" e + // let c = {| a = 10 |} + // let test = <@ fun (a : MyUnion) -> match a with | Single a -> a | Values (a,b) -> a + b + c.a @> + // failwithf "yeah: %A" test + // Error: yeah: Lambda(a, Let(x, ValueWithName(10, b), Call(None, op_Multiply, [x, a]))) + + + testCase "megaquote" <| fun () -> + let sepp = + <@ + let mutable b = { x = 10; y = 3 } + let mutable a = 10 + while a < 100 do + a <- a + 1 + b <- { b with x = b.x + a / 2 } + + for i in 0 .. 10 do + a <- a / 2 + + b + @> + + let t = + match <@ Microsoft.FSharp.Core.Operators.abs @> with + | Lambda(_, Call(None, meth, _)) -> meth.DeclaringType + | _ -> failwith "not a call" + + let str = t.GetMethods() |> Array.map (string) |> String.concat "\n" + + failwith str - failwithf "prop: %A" test ] \ No newline at end of file From 538137e71dce45c86d72371238c672fb1e790742 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Wed, 17 Apr 2019 09:41:49 +0200 Subject: [PATCH 17/38] String: implemented FromBase64String/ToBase64String from first principles (avoiding Unicode problems) --- src/fable-library/String.ts | 107 +++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/src/fable-library/String.ts b/src/fable-library/String.ts index 14b93bd69f..f880732341 100644 --- a/src/fable-library/String.ts +++ b/src/fable-library/String.ts @@ -374,64 +374,83 @@ export function arrayToGuid(buf: ArrayLike) { _byteToHex[buf[14]] + _byteToHex[buf[15]]; } -function notSupported(name: string): never { - throw new Error("The environment doesn't support '" + name + "', please use a polyfill."); +function valueToCharCode(c: number) { + if (c < 26) { + return 65 + c; + } else if (c < 52) { + return 97 + (c - 26); + } else if (c < 62) { + return 48 + (c - 52); + } else if (c === 62) { + return 43; + } else if (c === 63) { + return 47; + } else { + return 0; + } +} +function charCodeToValue(c: number) { + if (c >= 65 && c <= 90) { + return c - 65; + } else if (c >= 97 && c <= 122) { + return 26 + c - 97; + } else if (c >= 48 && c <= 57) { + return 52 + c - 48; + } else if (c === 43) { + return 62; + } else if (c === 47) { + return 63; + } else { + return 0; + } } export function toBase64String(inArray: number[]) { - let str = ""; - for (let i = 0; i < inArray.length; i++) { - str += String.fromCharCode(inArray[i]); - } - return typeof btoa === "function" ? btoa(str) : notSupported("btoa"); + let res = ""; + let i = 0; + while (i < inArray.length - 3) { + const b0 = inArray[i++]; + const b1 = inArray[i++]; + const b2 = inArray[i++]; + const c0 = valueToCharCode((b0 >> 2)); + const c1 = valueToCharCode(((b0 & 0x3) << 4) | (b1 >> 4)); + const c2 = valueToCharCode(((b1 & 0xF) << 2) | (b2 >> 6)); + const c3 = valueToCharCode(b2 & 0x3F); + res += String.fromCharCode(c0, c1, c2, c3); + } + + const missing = inArray.length - i; + const b0 = inArray[i++]; + const b1 = i < inArray.length ? inArray[i++] : 0; + const b2 = i < inArray.length ? inArray[i++] : 0; + const c0 = valueToCharCode((b0 >> 2)); + const c1 = valueToCharCode(((b0 & 0x3) << 4) | (b1 >> 4)); + const c2 = missing < 2 ? 61 : valueToCharCode(((b1 & 0xF) << 2) | (b2 >> 6)); + const c3 = missing < 3 ? 61 : valueToCharCode(b2 & 0x3F); + res += String.fromCharCode(c0, c1, c2, c3); + return res; } - - export function fromBase64String(b64Encoded: string) { - function toValue(c: number) { - if (c >= 65 && c <= 90) { - return c - 65; - } else if (c >= 97 && c <= 122) { - return 26 + c - 97; - } else if (c >= 48 && c <= 57) { - return 52 + c - 48; - } else if (c === 43) { - return 62; - } else if (c === 47) { - return 63; - } else { - return 0; - } - } let pad = 0; - while (b64Encoded.charAt(b64Encoded.length - 1 - pad) === "=") { + while (b64Encoded.charCodeAt(b64Encoded.length - 1 - pad) === 61) { pad = pad + 1; } - const length = 3 * b64Encoded.length / 4 - pad; - + const length = 3 * (0 | b64Encoded.length / 4) - pad; const bytes = new Uint8Array(length); let o = 0; - for (let i = 0; i < bytes.length; i += 3) { - const c0 = toValue(b64Encoded.charCodeAt(o)); - const c1 = toValue(b64Encoded.charCodeAt(o + 1)); - const c2 = toValue(b64Encoded.charCodeAt(o + 2)); - const c3 = toValue(b64Encoded.charCodeAt(o + 3)); - - const int = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3; - - bytes[i + 0] = (int >> 16) & 0xFF; - if (i + 1 < bytes.length) { bytes[i + 1] = (int >> 8) & 0xFF; } - if (i + 2 < bytes.length) { bytes[i + 2] = (int) & 0xFF; } - o += 4; + let i = 0; + while (i < bytes.length) { + const c0 = charCodeToValue(b64Encoded.charCodeAt(o++)); + const c1 = charCodeToValue(b64Encoded.charCodeAt(o++)); + const c2 = charCodeToValue(b64Encoded.charCodeAt(o++)); + const c3 = charCodeToValue(b64Encoded.charCodeAt(o++)); + bytes[i++] = ((c0 << 2) | (c1 >> 4)) & 0xFF; + if (i < bytes.length) { bytes[i++] = ((c1 << 4) | (c2 >> 2)) & 0xFF; } + if (i < bytes.length) { bytes[i++] = ((c2 << 6) | c3) & 0xFF; } } - // const binary = typeof atob === "function" ? atob(b64Encoded) : notSupported("atob"); - // const bytes = new Uint8Array(binary.length); - // for (let i = 0; i < binary.length; i++) { - // bytes[i] = binary.charCodeAt(i); - // } return bytes; } From e4a41bb7ba8128073327b4d0f6aeee6b3b0c0b0b Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Thu, 18 Apr 2019 08:18:44 +0200 Subject: [PATCH 18/38] sync --- src/Fable.Transforms/FSharp2Fable.Util.fs | 6 +++--- src/Fable.Transforms/FSharp2Fable.fs | 6 +++++- src/Fable.Transforms/Replacements.fs | 2 ++ src/fable-library/Reflection.ts | 2 +- tests/Main/ExprTests.fs | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index e6d08c56e3..a9f01c86ed 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -102,12 +102,12 @@ module Helpers = let getModuleReflectionName (com: ICompiler) (ent : FSharpEntity) = if ent.IsFSharpModule then let name = - (getEntityMangledName com false ent, Naming.NoMemberPart) + (getEntityMangledName com true ent, Naming.NoMemberPart) ||> Naming.sanitizeIdent (fun _ -> false) if name = "" then - None + Some "" else - Some ("Module$" + name) + Some name else None diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 94eda20515..d3e706516e 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -272,7 +272,11 @@ let rec private transformDecisionTargets (com: IFableCompiler) (ctx: Context) ac let private skipAttribute (name : string) = // TODO: skip all attributes where definiton not known??? - name.StartsWith "Microsoft.FSharp.Core" || name.StartsWith "System.Reflection" + name.StartsWith "Microsoft.FSharp.Core" || + name.StartsWith "System.Reflection" || + name.StartsWith "System.Runtime.CompilerServices" || + name.StartsWith "System.ObsoleteAttribute" || + name.StartsWith "System.Diagnostics" let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = match a.AttributeType.TryFullName with diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index f617f3f179..857de2ab2d 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2766,6 +2766,8 @@ let private replacedModules = "Microsoft.FSharp.Quotations.FSharpExpr", exprs "Expr" "Microsoft.FSharp.Quotations.FSharpVar", exprs "Var" "Microsoft.FSharp.Quotations.PatternsModule", exprs "Patterns" + "Microsoft.FSharp.Quotations.DerivedPatternsModule", exprs "DerivedPatterns" + "Microsoft.FSharp.Quotations.ExprShapeModule", exprs "ExprShape" "System.Math", operators "Microsoft.FSharp.Core.Operators", operators "Microsoft.FSharp.Core.Operators.Checked", operators diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 35f48311c3..e3981c3e6a 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1095,7 +1095,7 @@ export function createMethod(decl: NTypeInfo, name: string, mpars: string[], mar return found.get_IsGenericMethod() ? found.MakeGenericMethod(margs) : found; } else { const pp = mpars.map ((n) => getGenericParameter(n)); - const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => null), []); + const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => { throw new Error("cannot invoke"); }), []); decl.mems.push(meth); return meth.get_IsGenericMethod() ? meth.MakeGenericMethod(margs) : meth; } diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 391ffa33fd..a194aea99b 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -228,7 +228,7 @@ let tests = b <- { b with x = b.x + a / 2 } for i in 0 .. 10 do - a <- a / 2 + a <- a / i b @> @@ -240,6 +240,6 @@ let tests = let str = t.GetMethods() |> Array.map (string) |> String.concat "\n" - failwith str + failwith (string sepp) ] \ No newline at end of file From 0f225b895c9df80711bffe9ab2dde58e0d24a2c6 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Fri, 19 Apr 2019 12:08:29 +0200 Subject: [PATCH 19/38] various reflection fixes --- src/Fable.Transforms/AST/AST.Fable.fs | 4 +- src/Fable.Transforms/FSharp2Fable.fs | 87 ++++++-- src/Fable.Transforms/Fable2Babel.fs | 39 +++- src/Fable.Transforms/QuotationPickler.fs | 253 ++++++++++++++--------- src/fable-library/ExprUtils.fs | 31 +-- src/fable-library/Quotations.fs | 2 +- src/fable-library/Reflection.ts | 18 +- tests/Main/ExprTests.fs | 22 +- 8 files changed, 313 insertions(+), 143 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index d58be3f609..ea8dcfaa4c 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -63,8 +63,8 @@ type ParameterInfo = } type MemberInfoKind = - | Property of name : string * typ : Type * fsharp : bool * isStatic : bool - | Field of name : string * typ : Type * isStatic : bool + | Property of name : string * typ : Type * fsharp : bool * isStatic : bool * get : Option * set : Option + | Field of name : string * typ : Type * isStatic : bool * get : Option | Method of genericParameters : string[] * name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string | Constructor of parameters : ParameterInfo[] * mangledName : string | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string * mangledTypeName : string diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index d3e706516e..28a74aad61 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -311,18 +311,23 @@ let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharp | _ -> None -let private transformUnionCases (com:IFableCompiler) ctx (cases : seq) = - cases |> Seq.toArray |> Array.map (fun c -> - let fields = - c.UnionCaseFields |> Seq.toArray |> Array.map (fun f -> - { - Fable.MemberInfo.Kind = Fable.Property(f.Name, makeType com ctx.GenericArgs f.FieldType, true, false) - Fable.Attributes = f.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - ) +let private makeLambda (argType : Fable.Type) (body : Fable.Expr -> Fable.Expr) = + let target = { Fable.Ident.Name = "target"; Fable.Ident.Type = argType; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let body = body (Fable.IdentExpr target) + Fable.Function(Fable.FunctionKind.Lambda(target), body, None) +let private makeLambda2 (a0 : Fable.Type) (a1 : Fable.Type) (body : Fable.Expr -> Fable.Expr -> Fable.Expr) = + let v0 = { Fable.Ident.Name = "arg0"; Fable.Ident.Type = a0; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let v1 = { Fable.Ident.Name = "arg1"; Fable.Ident.Type = a1; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let body = body (Fable.IdentExpr v0) (Fable.IdentExpr v1) + Fable.Function(Fable.FunctionKind.Delegate [v0; v1], body, None) + +let private makeLambda0 (body : Fable.Expr -> Fable.Expr) = + let target = { Fable.Ident.Name = "target"; Fable.Ident.Type = Fable.Type.Any; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let body = body (Fable.IdentExpr target) + Fable.Function(Fable.FunctionKind.Lambda(target), body, None) + + - { Fable.UnionCaseInfo.Name = c.Name; Fable.UnionCaseInfo.Fields = fields } - ) let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFunctionOrValue) = @@ -331,13 +336,58 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun // TODO: support these? None elif m.IsValue then + + let get = + makeLambda Fable.Type.Any (fun t -> + if m.IsInstanceMember then + let ft = makeType com ctx.GenericArgs m.FullType + Fable.Get(t, Fable.FieldGet(m.CompiledName, m.IsMutable, ft), ft, None) + else + Util.makeValueFrom com ctx None m + ) |> Some + + let set = + if m.IsMutable then + makeLambda2 Fable.Type.Any Fable.Type.Any (fun t v -> + if m.IsInstanceMember then + let ft = makeType com ctx.GenericArgs m.FullType + Fable.Set(t, Fable.FieldSet(m.CompiledName, ft), v, None) + else + let ref = Util.memberRef com ctx None m + Fable.Set(ref, Fable.SetKind.VarSet, v, None) + ) |> Some + else + None + + Some { - Fable.MemberInfo.Kind = Fable.Field(m.CompiledName, makeType com ctx.GenericArgs m.FullType, not m.IsInstanceMember) + Fable.MemberInfo.Kind = Fable.Property(m.CompiledName, makeType com ctx.GenericArgs m.FullType, true, not m.IsInstanceMember, get, set) Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } elif m.IsProperty then + let ft = makeType com ctx.GenericArgs m.ReturnParameter.Type + + let get = + if m.HasGetterMethod && m.GetterMethod.GenericParameters.Count = 0 && (m.GetterMethod.CurriedParameterGroups |> Seq.sumBy (fun g -> g.Count)) = 0 then + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + makeLambda decl (fun t -> + let callee = if m.IsInstanceMember then Some t else None + makeCallFrom com ctx None ft false [] callee [] m.GetterMethod + ) |> Some + else + None + let set = + if m.HasSetterMethod && m.SetterMethod.GenericParameters.Count = 0 && (m.SetterMethod.CurriedParameterGroups |> Seq.sumBy (fun g -> g.Count)) = 1 then + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + + makeLambda2 decl ft (fun t value -> + let callee = if m.IsInstanceMember then Some t else None + makeCallFrom com ctx None ft false [] callee [value] m.SetterMethod + ) |> Some + else + None Some { - Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, makeType com ctx.GenericArgs m.ReturnParameter.Type, false, not m.IsInstanceMember) + Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, ft, false, not m.IsInstanceMember, get, set) Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } elif m.IsConstructor then @@ -355,8 +405,12 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun else // m.IsPropertyGetterMethod || m.IsPropertySetterMethod || m.IsValCompiledAsMethod || m.IsInstanceMember then let pars = - m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> - { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.choose (fun p -> + let t = makeType com ctx.GenericArgs p.Type + if t <> Fable.Type.Unit then + Some { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = t } + else + None ) let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type let mangledName = Helpers.getMemberDeclarationName com m @@ -953,8 +1007,9 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar let ctor = "new " + getEntityDeclarationName com ent Seq.concat [ ent.FSharpFields |> Seq.map (fun m -> + let ft = makeType com ctx.GenericArgs m.FieldType { - Fable.MemberInfo.Kind = Fable.Property(m.Name, makeType com ctx.GenericArgs m.FieldType, true, false) + Fable.MemberInfo.Kind = Fable.Property(m.Name, ft, true, false, None, None) Fable.Attributes = m.PropertyAttributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } ) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index fc0e5de49d..66cfcadb58 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -399,15 +399,30 @@ module Util = NewExpression(meth, [|self; genPars; StringLiteral name; ArrayExpression parameters; ret; BooleanLiteral isStatic; invoke; attributes |]) :> Expression - let newProperty (self : Expression) (isStatic : bool) (isFSharp : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) = + let newProperty (self : Expression) (isStatic : bool) (isFSharp : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (get : Option) (set : Option) = let prop = coreValue com ctx "Reflection" "NPropertyInfo" let ret = transformTypeInfo com ctx r [||] genMap ret - NewExpression(prop, [|self; StringLiteral name; ret; BooleanLiteral isStatic; BooleanLiteral isFSharp; attributes|]) :> Expression - let newField (self : Expression) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) = + let args = [|self; StringLiteral name :> Expression; ret; BooleanLiteral isStatic :> Expression; BooleanLiteral isFSharp :> Expression; attributes :> Expression|] + + let args = + match get, set with + | Some get, Some set -> Array.append args [| get; set |] + | Some get, None -> Array.append args [| get |] + | None, Some set -> Array.append args [| NullLiteral() :> Expression; set |] + | None, None -> args + + NewExpression(prop, args) :> Expression + + + let newField (self : Expression) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (get : Option) = let fld = coreValue com ctx "Reflection" "NFieldInfo" let ret = transformTypeInfo com ctx r [||] genMap ret - NewExpression(fld, [|self; StringLiteral name; ret; BooleanLiteral isStatic; attributes|]) :> Expression + match get with + | Some get -> + NewExpression(fld, [|self; StringLiteral name; ret; BooleanLiteral isStatic; attributes; get|]) :> Expression + | None -> + NewExpression(fld, [|self; StringLiteral name; ret; BooleanLiteral isStatic; attributes|]) :> Expression mems |> Array.map (fun x -> let attributes = ArrayExpression (x.Attributes |> Array.map (fun (fullname, e) -> @@ -454,11 +469,14 @@ module Util = newMethod self genericParameters isStatic ret name attributes pars invoke - | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic) -> - newProperty self isStatic fsharp typ name attributes + | Fable.MemberInfoKind.Property(name, typ, fsharp, isStatic, get, set) -> + let get = get |> Option.map (fun g -> com.TransformAsExpr(ctx, g)) + let set = set |> Option.map (fun g -> com.TransformAsExpr(ctx, g)) + newProperty self isStatic fsharp typ name attributes get set - | Fable.MemberInfoKind.Field(name, typ, isStatic) -> - newField self isStatic typ name attributes + | Fable.MemberInfoKind.Field(name, typ, isStatic, get) -> + let get = get |> Option.map (fun g -> com.TransformAsExpr(ctx, g)) + newField self isStatic typ name attributes get ) and transformRecordReflectionInfo (com : IBabelCompiler) ctx r (ent: FSharpEntity) declaringName (mems : Fable.MemberInfo[]) generics = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo @@ -982,7 +1000,8 @@ module Util = jsInstanceof (coreValue com ctx "Types" "List") expr | Fable.Expr _ -> - coreLibCall com ctx None "ExprUtils" "isExpr" [| com.TransformAsExpr(ctx, expr) |] + jsInstanceof (coreValue com ctx "Quotations" "FSharpExpr") expr + //coreLibCall com ctx None "ExprUtils" "isExpr" [| com.TransformAsExpr(ctx, expr) |] | Replacements.Builtin kind -> match kind with @@ -1004,6 +1023,8 @@ module Util = | Replacements.FSharpReference _ -> fail "result/choice/reference" | Fable.DeclaredType (ent, genArgs) -> match ent.TryFullName with + | Some "Microsoft.FSharp.Quotations.FSharpExpr" + | Some "Microsoft.FSharp.Quotations.FSharpExpr`1" -> jsInstanceof (coreValue com ctx "Quotations" "FSharpExpr") expr //coreLibCall com ctx None "ExprUtils" "isExpr" [| com.TransformAsExpr(ctx, expr) |] | Some "System.Type" -> coreLibCall com ctx None "Reflection" "isType" [|com.TransformAsExpr(ctx, expr)|] | Some "System.Reflection.MemberInfo" -> coreLibCall com ctx None "Reflection" "isMemberInfo" [|com.TransformAsExpr(ctx, expr)|] | Some "System.Reflection.MethodBase" -> coreLibCall com ctx None "Reflection" "isMethodBase" [|com.TransformAsExpr(ctx, expr)|] diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index 7d3e8680b1..2850bace41 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -209,8 +209,50 @@ module Pickler = | h :: _ -> h.[i] | _ -> failwith "invalid case" ) +let rec propertyGetS (tid : int) (target : Option) (name : string) (index : list) (ret : int) = + state { + match target with + | Some target -> + do! Pickler.write 18uy + do! Pickler.write tid + do! Pickler.write name + do! Pickler.write index.Length + for i in index do do! serializeS i + do! Pickler.write ret + do! serializeS target + | None -> + do! Pickler.write 19uy + do! Pickler.write tid + do! Pickler.write name + do! Pickler.write index.Length + for i in index do do! serializeS i + do! Pickler.write ret + } + +and propertySetS (tid : int) (target : Option) (name : string) (index : list) (value : FSharpExpr) = + state { + let! ret = Pickler.useType value.Type + match target with + | Some target -> + do! Pickler.write 20uy + do! Pickler.write tid + do! Pickler.write name + do! Pickler.write index.Length + for i in index do do! serializeS i + do! Pickler.write ret + do! serializeS target + do! serializeS value + | None -> + do! Pickler.write 21uy + do! Pickler.write tid + do! Pickler.write name + do! Pickler.write index.Length + for i in index do do! serializeS i + do! Pickler.write ret + do! serializeS value + } -let rec serializeS (expr : FSharpExpr) = +and serializeS (expr : FSharpExpr) = state { match expr with | BasicPatterns.Lambda(v, b) -> @@ -237,41 +279,15 @@ let rec serializeS (expr : FSharpExpr) = do! serializeS e do! serializeS b - | BasicPatterns.Call(target, m, targs, margs, args) -> - //let! mem = Pickler.useMember m targs margs - let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs - let! rid = Pickler.useType m.ReturnParameter.Type - let! margs = margs |> List.mapS Pickler.useType - let mpars = m.GenericParameters |> Seq.map (fun p -> p.Name) |> Seq.toArray - let! aids = m.CurriedParameterGroups |> Seq.concat |> Seq.toList |> List.mapS (fun p -> Pickler.useType p.Type) - match target with - | Some target -> - do! Pickler.write 5uy - do! Pickler.write tid - do! Pickler.write m.CompiledName - do! Pickler.write mpars - do! Pickler.write margs - do! Pickler.write aids - do! Pickler.write rid - do! Pickler.write args.Length - - do! serializeS target - for a in args do - do! serializeS a - - | _ -> - do! Pickler.write 6uy - do! Pickler.write tid - do! Pickler.write m.CompiledName - do! Pickler.write mpars - do! Pickler.write margs - do! Pickler.write aids - do! Pickler.write rid - do! Pickler.write args.Length - - for a in args do - do! serializeS a + | BasicPatterns.FSharpFieldGet(target, typ, field) -> + let! tid = Pickler.useType typ + let! ret = Pickler.useType field.FieldType + do! propertyGetS tid target field.Name [] ret + | BasicPatterns.FSharpFieldSet(target, typ, field, value) -> + let! tid = Pickler.useType typ + do! propertySetS tid target field.Name [] value + | BasicPatterns.AddressOf e -> do! Pickler.write 7uy do! serializeS e @@ -343,74 +359,17 @@ let rec serializeS (expr : FSharpExpr) = do! serializeS e do! serializeS b - | BasicPatterns.FSharpFieldGet(target, typ, field) -> - let! tid = Pickler.useType typ - let! ret = Pickler.useType field.FieldType - match target with - | Some target -> - do! Pickler.write 18uy - do! Pickler.write tid - do! Pickler.write field.Name - do! Pickler.write ret - do! serializeS target - | None -> - do! Pickler.write 19uy - do! Pickler.write tid - do! Pickler.write field.Name - do! Pickler.write ret - - | BasicPatterns.FSharpFieldSet(target, typ, field, value) -> - let! tid = Pickler.useType typ - let! ret = Pickler.useType field.FieldType - match target with - | Some target -> - do! Pickler.write 20uy - do! Pickler.write tid - do! Pickler.write field.Name - do! Pickler.write ret - do! serializeS target - do! serializeS value - | None -> - do! Pickler.write 21uy - do! Pickler.write tid - do! Pickler.write field.Name - do! Pickler.write ret - do! serializeS value + | BasicPatterns.ILFieldGet(target, typ, field) -> let! tid = Pickler.useType typ let! ret = Pickler.useType expr.Type - match target with - | Some target -> - do! Pickler.write 18uy - do! Pickler.write tid - do! Pickler.write field - do! Pickler.write ret - do! serializeS target - | None -> - do! Pickler.write 19uy - do! Pickler.write tid - do! Pickler.write field - do! Pickler.write ret + do! propertyGetS tid target field [] ret | BasicPatterns.ILFieldSet(target, typ, field, value) -> let! tid = Pickler.useType typ - let! ret = Pickler.useType value.Type - match target with - | Some target -> - do! Pickler.write 20uy - do! Pickler.write tid - do! Pickler.write field - do! Pickler.write ret - do! serializeS target - do! serializeS value - | None -> - do! Pickler.write 21uy - do! Pickler.write tid - do! Pickler.write field - do! Pickler.write ret - do! serializeS value - + do! propertySetS tid target field [] value + | BasicPatterns.LetRec(vs, b) -> do! Pickler.write 22uy do! Pickler.write vs.Length @@ -522,6 +481,108 @@ let rec serializeS (expr : FSharpExpr) = do! serializeS target do! Pickler.popCases + + | BasicPatterns.Call(target, m, targs, margs, args) -> + let args = + match args with + | [unitArg] when Helpers.isUnit unitArg.Type -> [] + | args -> args + + if m.IsValue && List.isEmpty args && List.isEmpty margs && Option.isSome m.DeclaringEntity && m.DeclaringEntity.Value.IsFSharpModule then + let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + let! ret = Pickler.useType m.ReturnParameter.Type + do! propertyGetS tid target m.CompiledName [] ret + + //elif m.IsExtensionMember then + + // if m.IsPropertyGetterMethod then + // let name = + // let name = m.LogicalName + // if name.StartsWith "get_" then name.Substring(4) + // else name + + // let args = + // match args with + // | unitVal :: rest when Helpers.isUnit unitVal.Type -> rest + // | args -> args + // let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + // let! ret = Pickler.useType m.ReturnParameter.Type + // do! propertyGetS tid target name args ret + // else + + // failwithf "%A %A %A %A %A %A" m.DisplayName m.CompiledName m.LogicalName m.FullName m.IsPropertyGetterMethod m.IsPropertySetterMethod + + elif not m.IsExtensionMember && m.IsPropertyGetterMethod then + let name = + let name = m.CompiledName + if name.StartsWith "get_" then name.Substring(4) + else name + + let args = + match args with + | unitVal :: rest when Helpers.isUnit unitVal.Type -> rest + | args -> args + + let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + let! ret = Pickler.useType m.ReturnParameter.Type + do! propertyGetS tid target name args ret + + elif not m.IsExtensionMember && m.IsPropertySetterMethod then + let name = + let name = m.CompiledName + if name.StartsWith "set_" then name.Substring(4) + else name + + let args = + match args with + | unitVal :: rest when Helpers.isUnit unitVal.Type -> rest + | args -> args + + let idx, value = + match args with + | [] -> failwith "bad" + | _ -> + let value = List.last args + let idx = List.take (args.Length - 1) args + idx, value + + let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + do! propertySetS tid target name idx value + else + //let! mem = Pickler.useMember m targs margs + let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs + let! rid = Pickler.useType m.ReturnParameter.Type + let! margs = margs |> List.mapS Pickler.useType + let mpars = m.GenericParameters |> Seq.map (fun p -> p.Name) |> Seq.toArray + let! aids = m.CurriedParameterGroups |> Seq.concat |> Seq.toList |> List.mapS (fun p -> Pickler.useType p.Type) + match target with + | Some target -> + do! Pickler.write 5uy + do! Pickler.write tid + do! Pickler.write m.CompiledName + do! Pickler.write mpars + do! Pickler.write margs + do! Pickler.write aids + do! Pickler.write rid + do! Pickler.write args.Length + + do! serializeS target + for a in args do + do! serializeS a + + | _ -> + do! Pickler.write 6uy + do! Pickler.write tid + do! Pickler.write m.CompiledName + do! Pickler.write mpars + do! Pickler.write margs + do! Pickler.write aids + do! Pickler.write rid + do! Pickler.write args.Length + + for a in args do + do! serializeS a + | _ -> do! Pickler.write 255uy do! Pickler.write (sprintf "BAD EXPRESSION: %A" expr) diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index ab6a26bc23..62ca7a96fc 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -243,33 +243,39 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty | 18uy -> let typ = types.[stream.ReadInt32()] let name = stream.ReadString() + let cidx = stream.ReadInt32() + let idx = init cidx (fun _ -> read()) let ret = types.[stream.ReadInt32()] let target = read() - + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertyGet(target, prop) + Expr.PropertyGet(target, prop, idx) | None -> let prop = createRecordProperty typ name ret - Expr.PropertyGet(target, prop) + Expr.PropertyGet(target, prop, idx) | 19uy -> let typ = types.[stream.ReadInt32()] let name = stream.ReadString() + let cidx = stream.ReadInt32() + let idx = init cidx (fun _ -> read()) let ret = types.[stream.ReadInt32()] let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertyGet(prop) + Expr.PropertyGet(prop, idx) | None -> let prop = createStaticProperty typ name ret - Expr.PropertyGet(prop) + Expr.PropertyGet(prop, idx) | 20uy -> let typ = types.[stream.ReadInt32()] let name = stream.ReadString() + let cidx = stream.ReadInt32() + let idx = init cidx (fun _ -> read()) let ret = types.[stream.ReadInt32()] let target = read() let value = read() @@ -277,24 +283,26 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertySet(target, prop, value) + Expr.PropertySet(target, prop, value, idx) | None -> let prop = createRecordProperty typ name ret - Expr.PropertySet(target, prop, value) + Expr.PropertySet(target, prop, value, idx) | 21uy -> let typ = types.[stream.ReadInt32()] let name = stream.ReadString() + let cidx = stream.ReadInt32() + let idx = init cidx (fun _ -> read()) let ret = types.[stream.ReadInt32()] let value = read() let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertySet(prop, value) + Expr.PropertySet(prop, value, idx) | None -> let prop = createStaticProperty typ name ret - Expr.PropertySet(prop, value) + Expr.PropertySet(prop, value, idx) | 22uy -> let cnt = stream.ReadInt32() @@ -384,8 +392,3 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty failwithf "invalid expression: %A at %A" tag stream.Position read() - -let isExpr (o : obj) = - match o with - | :? Expr -> true - | _ -> false \ No newline at end of file diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 24f47ce624..9447ab9e02 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -655,7 +655,7 @@ module Patterns = let checkTypesSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) = if (expectedType <> receivedType) then - invalidArg "receivedType" (String.Format("{0}/{1}: {1} {2}", threeHoleSR, name, expectedType.FullName, receivedType.FullName)) + invalidArg "receivedType" (String.Format("{0}/{1}: {2} {3}", threeHoleSR, name, expectedType.FullName, receivedType.FullName)) let checkTypesWeakSR (expectedType: Type) (receivedType : Type) name (threeHoleSR : string) = () diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index e3981c3e6a..cdd6201731 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -114,7 +114,7 @@ export class NMethodInfo extends NMethodBase { ) { super(declaringType, name, parameters.map((a) => a), isStatic, attributes); - if (!returnType) { throw new Error(`MethodInfo ${name} does not have a return type`); } + if (!returnType) { throw new Error(`MethodInfo ${declaringType.toFullString()} ${name} does not have a return type`); } genericArguments = genericArguments || []; const isDef = genericArguments.findIndex((p) => p.isGenericParameter) >= 0; @@ -130,7 +130,7 @@ export class NMethodInfo extends NMethodBase { } this.Parameters.forEach((p) => { p.ParameterType = this.ResolveGeneric(p.ParameterType); }); this.ReturnType = returnType.get_ContainsGenericParameters() ? this.ResolveGeneric(returnType) : returnType; - if (!this.ReturnType) { throw new Error(`MethodInfo ${name} does not have a return type`); } + if (!this.ReturnType) { throw new Error(`MethodInfo ${declaringType.toFullString()} ${name} does not have a return type`); } } @@ -252,6 +252,7 @@ export class NFieldInfo extends NMemberInfo { type: NTypeInfo, public IsStatic: boolean, attributes: CustomAttribute[], + private get?: (t: any) => any, ) { super(declaringType, name, attributes); @@ -276,6 +277,14 @@ export class NFieldInfo extends NMemberInfo { return attPrefix + prefix + this.Name + " : " + typ; } + public GetValue(target: any) { + if (this.get) { + return this.get(target); + } else { + throw new Error("cannot get field " + this.toPrettyString()); + } + } + public toString() { return this.toPrettyString(); } @@ -796,7 +805,8 @@ function selectMany(input: TIn[], selectListFn: (t: TIn, i: number) = }, new Array()); } -export function tuple(...generics: NTypeInfo[]): NTypeInfo { +export function tuple(...generics: any[]): NTypeInfo { + if (generics.length === 1) { generics = generics[0] as NTypeInfo[]; } if (generics.length === 0) { throw new Error("empty tuple"); } const name = "System.Tuple`" + generics.length; const gen = @@ -1095,7 +1105,7 @@ export function createMethod(decl: NTypeInfo, name: string, mpars: string[], mar return found.get_IsGenericMethod() ? found.MakeGenericMethod(margs) : found; } else { const pp = mpars.map ((n) => getGenericParameter(n)); - const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => { throw new Error("cannot invoke"); }), []); + const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => { throw new Error("cannot invoke " + decl.fullname + "." + name); }), []); decl.mems.push(meth); return meth.get_IsGenericMethod() ? meth.MakeGenericMethod(margs) : meth; } diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index a194aea99b..d85f2151f3 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -61,15 +61,28 @@ type System.Type with member x.ToPrettyString() : string = jsNative type Sepp(a : int, b : string) = - member x.Yeah = b + member x.Yeah + with get() = b + and set (_ : string) = () + member x.DoIt(c : int) = a*c + member x.Item + with get(i : int) = a + i + and set (i : int) (v : int) = () type MyUnion = | Values of int * int | Single of value : int +module MyModule = + let sepp = Values(1,1) + +[] +module Blubber = + type Sepp with + member x.A = 10 let tests = testList "Expr" [ @@ -217,6 +230,13 @@ let tests = // failwithf "yeah: %A" test // Error: yeah: Lambda(a, Let(x, ValueWithName(10, b), Call(None, op_Multiply, [x, a]))) + testCase "bla" <| fun () -> + match <@@ fun (v : Sepp) -> v.A @@> with + | Lambda (_, Call(t, meth, args)) -> + let res = meth.Invoke(Sepp(1, "a"), [||]) + failwithf "yeah: %A //// %A //// %A //// %A" (string t) (string meth) (string args) (string res) + | _ -> + () testCase "megaquote" <| fun () -> let sepp = From 56e71f4df7ad32ff3ea6d35f923acd452c55a379 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 12:18:43 +0200 Subject: [PATCH 20/38] standalone working again --- src/Fable.Transforms/QuotationPickler.fs | 349 ++++++++++-------- .../src/Fable.Standalone.fsproj | 1 + 2 files changed, 194 insertions(+), 156 deletions(-) diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index 2850bace41..80d049f926 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -1,14 +1,58 @@ module Fable.Transforms.FSharp2Fable.QuotationPickler -open System.Collections.Generic -open FSharp.Compiler.Ast open FSharp.Compiler.SourceCodeServices -open System.IO type MemberDescription = | Member of FSharpMemberOrFunctionOrValue * list * list | UnionCase of FSharpUnionCase * list +type BinaryWriter() = + + static let nextPowerOfTwo (v : int) = + let mutable x = v - 1 + x <- x ||| (x >>> 1) + x <- x ||| (x >>> 2) + x <- x ||| (x >>> 4) + x <- x ||| (x >>> 8) + x <- x ||| (x >>> 16) + x + 1 + + let mutable arr : byte[] = Array.zeroCreate 16 + let mutable position = 0 + + member x.Write(bytes : byte[], offset : int, length : int) = + let len = position + length + if len > arr.Length then + let cap = nextPowerOfTwo len + let n = Array.zeroCreate cap + for i in 0 .. position - 1 do n.[i] <- arr.[i] + arr <- n + + let mutable i = offset + let mutable o = position + for c in 0 .. length - 1 do + arr.[o] <- bytes.[i] + o <- o + 1 + i <- i + 1 + position <- len + + member x.Write(bytes : byte[]) = + x.Write(bytes, 0, bytes.Length) + + member x.Write(i : uint8) = x.Write [| i |] + member x.Write(i : int8) = x.Write [| uint8 i |] + member x.Write(i : uint16) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : int16) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : uint32) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : int32) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : uint64) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : int64) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : float32) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : float) = x.Write (System.BitConverter.GetBytes i) + member x.Write(i : string) = x.Write (System.Text.Encoding.UTF8.GetBytes i) + + member x.ToByteArray() = arr.[0..position-1] + type PicklerState = { varId : int @@ -33,31 +77,31 @@ type PicklerState = type State<'s, 'a> = { run : 's -> 's * 'a } module State = - let get<'s, 'a> = { run = fun s -> s, s } - let put (s : 's) = { run = fun _ -> s, () } - let modify (f : 's -> 's) = { run = fun s -> f s, () } + let inline get<'s, 'a> = { run = fun s -> s, s } + let inline put (s : 's) = { run = fun _ -> s, () } + let inline modify (f : 's -> 's) = { run = fun s -> f s, () } - let map (f : 'a -> 'b) (m : State<'s, 'a>) = + let inline map (f : 'a -> 'b) (m : State<'s, 'a>) = { run = fun s -> let s, a = m.run s s, f a } - let bind (f : 'a -> State<'s, 'b>) (m : State<'s, 'a>) = + let inline bind (f : 'a -> State<'s, 'b>) (m : State<'s, 'a>) = { run = fun s -> let (s,a) = m.run s (f a).run s } - let value (v : 'a) = { run = fun s -> s, v } + let inline value (v : 'a) = { run = fun s -> s, v } type StateBuilder() = - member x.Bind(m : State<'s, 'a>, f : 'a -> State<'s, 'b>) = bind f m - member x.Return v = value v - member x.ReturnFrom(s : State<'s, 'a>) = s - member x.Zero() = value () - member x.Delay (f : unit -> State<'s, 'a>) = { run = fun s -> f().run s } - member x.Combine(l : State<'s, unit>, r : State<'s, 'a>) = l |> bind (fun () -> r) - member x.For(seq : seq<'a>, action : 'a -> State<'s, unit>) = + member inline x.Bind(m : State<'s, 'a>, f : 'a -> State<'s, 'b>) = bind f m + member inline x.Return v = value v + member inline x.ReturnFrom(s : State<'s, 'a>) = s + member inline x.Zero() = value () + member inline x.Delay (f : unit -> State<'s, 'a>) = { run = fun s -> f().run s } + member inline x.Combine(l : State<'s, unit>, r : State<'s, 'a>) = l |> bind (fun () -> r) + member inline x.For(seq : seq<'a>, action : 'a -> State<'s, unit>) = { run = fun s -> let mutable s = s for e in seq do @@ -65,7 +109,7 @@ module State = s <- s1 s, () } - member x.While(guard : unit -> bool, body : State<'s, unit>) = + member inline x.While(guard : unit -> bool, body : State<'s, unit>) = { run = fun s -> let mutable s = s while guard() do @@ -84,7 +128,7 @@ module List = f h |> State.bind (fun h -> mapS f t |> State.map (fun t -> h :: t)) module Pickler = - let newVar (l : FSharpMemberOrFunctionOrValue) = + let inline newVar (l : FSharpMemberOrFunctionOrValue) = { run = fun s -> { s with varId = s.varId + 1 @@ -92,15 +136,15 @@ module Pickler = }, s.varId } - let tryGetVar (l : FSharpMemberOrFunctionOrValue) = + let inline tryGetVar (l : FSharpMemberOrFunctionOrValue) = State.get |> State.map (fun s -> s.variables |> List.tryPick (fun (m, i) -> if m = l then Some i else None) ) - let getVar (l : FSharpMemberOrFunctionOrValue) = + let inline getVar (l : FSharpMemberOrFunctionOrValue) = tryGetVar l |> State.map Option.get - let useValue (l : FSharpMemberOrFunctionOrValue) = + let inline useValue (l : FSharpMemberOrFunctionOrValue) = { run = fun s -> let res = s.values |> List.tryPick (fun (v,i) -> if v = l then Some i else None) match res with @@ -110,7 +154,7 @@ module Pickler = let id = s.valueId { s with valueId = id + 1; values = (l, id) :: s.values }, id } - let useType (t : FSharpType) = + let inline useType (t : FSharpType) = { run = fun s -> let res = s.types |> List.tryPick (function (Choice1Of2 v,i) when v = t -> Some i | _ -> None) match res with @@ -120,7 +164,7 @@ module Pickler = let id = s.typeId { s with typeId = id + 1; types = (Choice1Of2 t, id) :: s.types }, id } - let useTypeDef (t : FSharpEntity) (targs : list) = + let inline useTypeDef (t : FSharpEntity) (targs : list) = { run = fun s -> let res = s.types |> List.tryPick (function (Choice2Of2(v,ta),i) when v = t && ta = targs -> Some i | _ -> None) match res with @@ -130,7 +174,7 @@ module Pickler = let id = s.typeId { s with typeId = id + 1; types = (Choice2Of2(t, targs), id) :: s.types }, id } - let useMember (mem : FSharpMemberOrFunctionOrValue) (targs : list) (margs : list) = + let inline useMember (mem : FSharpMemberOrFunctionOrValue) (targs : list) (margs : list) = { run = fun s -> let res = s.members |> List.tryPick (function (Member(v,t,m),i) when v = mem && t = targs && m = margs -> Some i | _ -> None) match res with @@ -140,7 +184,7 @@ module Pickler = let id = s.memberId { s with memberId = id + 1; members = (Member (mem, targs, margs), id) :: s.members }, id } - let useUnionCase (case : FSharpUnionCase) (targs : list) = + let inline useUnionCase (case : FSharpUnionCase) (targs : list) = { run = fun s -> let res = s.members |> List.tryPick (function (UnionCase(c,t),i) when c = case && t = targs -> Some i | _ -> None) match res with @@ -151,7 +195,7 @@ module Pickler = { s with memberId = id + 1; members = (UnionCase(case,targs), id) :: s.members }, id } - let useLiteral (v : obj) (t : FSharpType) = + let inline useLiteral (v : obj) (t : FSharpType) = { run = fun s -> let res = s.literals |> List.tryPick (fun (vi,ti,i) -> if vi = v && ti = t then Some i else None) match res with @@ -161,39 +205,33 @@ module Pickler = let id = s.literalId { s with literalId = id + 1; literals = (v, t, id) :: s.literals }, id } - type Writes private() = - static member Write(s : BinaryWriter, v : byte) = s.Write v - static member Write(s : BinaryWriter, v : byte[]) = s.Write v - static member Write(s : BinaryWriter, v : int8) = s.Write v - static member Write(s : BinaryWriter, v : uint16) = s.Write v - static member Write(s : BinaryWriter, v : int16) = s.Write v - static member Write(s : BinaryWriter, v : uint32) = s.Write v - static member Write(s : BinaryWriter, v : int32) = s.Write v - static member Write(s : BinaryWriter, v : string) = - let bytes = System.Text.Encoding.UTF8.GetBytes v - s.Write(bytes.Length) - s.Write bytes - static member Write(s : BinaryWriter, vs : seq) = - let vs = Seq.toArray vs - s.Write vs.Length - for v in vs do s.Write v - static member Write(s : BinaryWriter, v : string[]) = - Writes.Write(s, v.Length) - for str in v do Writes.Write(s, str) + let writeByte (b : byte) = { run = fun s -> s.writer.Write b; s, () } + let writeInt (b : int) = { run = fun s -> s.writer.Write b; s, () } - let inline private writeAux< ^a, ^b, ^c when (^a or ^b or ^c) : (static member Write : ^a * ^b -> unit)> (c : ^c) (a : ^a) (b : ^b) = - ((^a or ^b or ^c) : (static member Write : ^a * ^b -> unit) (a,b)) - - let inline write b = - let doit w b = writeAux Unchecked.defaultof w b + let writeString (v : string) = { run = fun s -> - doit s.writer b + let bytes = System.Text.Encoding.UTF8.GetBytes v + s.writer.Write(bytes.Length) + s.writer.Write bytes s, () - } + } + let writeStringArray (v : string[]) = + state { + do! writeInt v.Length + for s in v do do! writeString s - let pushCases (cs : array * FSharpExpr>) = + } + + let writeIntArray (vs : seq) = + { run = fun s -> + let vs = Seq.toArray vs + s.writer.Write vs.Length + for v in vs do s.writer.Write v + s, () + } + let inline pushCases (cs : array * FSharpExpr>) = State.modify (fun s -> { s with cases = cs :: s.cases }) let popCases = @@ -203,7 +241,7 @@ module Pickler = | _ -> s ) - let getCase (i : int) = + let inline getCase (i : int) = State.get |> State.map (fun s -> match s.cases with | h :: _ -> h.[i] @@ -213,20 +251,20 @@ let rec propertyGetS (tid : int) (target : Option) (name : string) ( state { match target with | Some target -> - do! Pickler.write 18uy - do! Pickler.write tid - do! Pickler.write name - do! Pickler.write index.Length + do! Pickler.writeByte 18uy + do! Pickler.writeInt tid + do! Pickler.writeString name + do! Pickler.writeInt index.Length for i in index do do! serializeS i - do! Pickler.write ret + do! Pickler.writeInt ret do! serializeS target | None -> - do! Pickler.write 19uy - do! Pickler.write tid - do! Pickler.write name - do! Pickler.write index.Length + do! Pickler.writeByte 19uy + do! Pickler.writeInt tid + do! Pickler.writeString name + do! Pickler.writeInt index.Length for i in index do do! serializeS i - do! Pickler.write ret + do! Pickler.writeInt ret } and propertySetS (tid : int) (target : Option) (name : string) (index : list) (value : FSharpExpr) = @@ -234,21 +272,21 @@ and propertySetS (tid : int) (target : Option) (name : string) (inde let! ret = Pickler.useType value.Type match target with | Some target -> - do! Pickler.write 20uy - do! Pickler.write tid - do! Pickler.write name - do! Pickler.write index.Length + do! Pickler.writeByte 20uy + do! Pickler.writeInt tid + do! Pickler.writeString name + do! Pickler.writeInt index.Length for i in index do do! serializeS i - do! Pickler.write ret + do! Pickler.writeInt ret do! serializeS target do! serializeS value | None -> - do! Pickler.write 21uy - do! Pickler.write tid - do! Pickler.write name - do! Pickler.write index.Length + do! Pickler.writeByte 21uy + do! Pickler.writeInt tid + do! Pickler.writeString name + do! Pickler.writeInt index.Length for i in index do do! serializeS i - do! Pickler.write ret + do! Pickler.writeInt ret do! serializeS value } @@ -257,25 +295,25 @@ and serializeS (expr : FSharpExpr) = match expr with | BasicPatterns.Lambda(v, b) -> let! var = Pickler.newVar v - do! Pickler.write 1uy - do! Pickler.write var + do! Pickler.writeByte 1uy + do! Pickler.writeInt var return! serializeS b | BasicPatterns.Value v -> match! Pickler.tryGetVar v with | Some var -> - do! Pickler.write 2uy - do! Pickler.write var + do! Pickler.writeByte 2uy + do! Pickler.writeInt var | None -> let! var = Pickler.useValue v - do! Pickler.write 3uy - do! Pickler.write var + do! Pickler.writeByte 3uy + do! Pickler.writeInt var | BasicPatterns.Let((v, e), b) -> let! var = Pickler.newVar v - do! Pickler.write 4uy - do! Pickler.write var + do! Pickler.writeByte 4uy + do! Pickler.writeInt var do! serializeS e do! serializeS b @@ -289,72 +327,72 @@ and serializeS (expr : FSharpExpr) = do! propertySetS tid target field.Name [] value | BasicPatterns.AddressOf e -> - do! Pickler.write 7uy + do! Pickler.writeByte 7uy do! serializeS e | BasicPatterns.AddressSet(v, e) -> - do! Pickler.write 8uy + do! Pickler.writeByte 8uy do! serializeS v do! serializeS e | BasicPatterns.AnonRecordGet(e, t, i) -> let fieldName = t.AnonRecordTypeDetails.SortedFieldNames.[i] let! typ = Pickler.useType t - do! Pickler.write 9uy - do! Pickler.write typ - do! Pickler.write fieldName + do! Pickler.writeByte 9uy + do! Pickler.writeInt typ + do! Pickler.writeString fieldName do! serializeS e | BasicPatterns.Application(e, ts, args) -> - do! Pickler.write 10uy + do! Pickler.writeByte 10uy do! serializeS e - do! Pickler.write args.Length + do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.Const(o, t) -> - do! Pickler.write 11uy + do! Pickler.writeByte 11uy let! vid = Pickler.useLiteral o t - do! Pickler.write vid + do! Pickler.writeInt vid | BasicPatterns.IfThenElse(c, i, e) -> - do! Pickler.write 12uy + do! Pickler.writeByte 12uy do! serializeS c do! serializeS i do! serializeS e | BasicPatterns.UnionCaseTest(expr, typ, case) -> - do! Pickler.write 13uy + do! Pickler.writeByte 13uy let! tid = Pickler.useType typ - do! Pickler.write tid - do! Pickler.write case.CompiledName + do! Pickler.writeInt tid + do! Pickler.writeString case.CompiledName do! serializeS expr | BasicPatterns.UnionCaseGet(target, typ, case, prop) -> let index = case.UnionCaseFields |> Seq.findIndex (fun pi -> pi = prop) let! tid = Pickler.useType typ - do! Pickler.write 14uy - do! Pickler.write tid - do! Pickler.write case.CompiledName - do! Pickler.write index + do! Pickler.writeByte 14uy + do! Pickler.writeInt tid + do! Pickler.writeString case.CompiledName + do! Pickler.writeInt index do! serializeS target | BasicPatterns.Coerce(t, e) -> let! tid = Pickler.useType t - do! Pickler.write 15uy - do! Pickler.write tid + do! Pickler.writeByte 15uy + do! Pickler.writeInt tid do! serializeS e | BasicPatterns.DefaultValue t -> let! tid = Pickler.useType t - do! Pickler.write 16uy - do! Pickler.write tid + do! Pickler.writeByte 16uy + do! Pickler.writeInt tid | BasicPatterns.FastIntegerForLoop(s, e, BasicPatterns.Lambda(v, b), true) -> let! vid = Pickler.newVar v - do! Pickler.write 17uy - do! Pickler.write vid + do! Pickler.writeByte 17uy + do! Pickler.writeInt vid do! serializeS s do! serializeS e do! serializeS b @@ -371,11 +409,11 @@ and serializeS (expr : FSharpExpr) = do! propertySetS tid target field [] value | BasicPatterns.LetRec(vs, b) -> - do! Pickler.write 22uy - do! Pickler.write vs.Length + do! Pickler.writeByte 22uy + do! Pickler.writeInt vs.Length for (v, e) in vs do let! vid = Pickler.newVar v - do! Pickler.write vid + do! Pickler.writeInt vid do! serializeS e do! serializeS b @@ -385,9 +423,9 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewArray(elementType, args) -> let! tid = Pickler.useType elementType - do! Pickler.write 24uy - do! Pickler.write tid - do! Pickler.write args.Length + do! Pickler.writeByte 24uy + do! Pickler.writeInt tid + do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.NewDelegate _ -> @@ -397,46 +435,46 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewObject(ctor, targs, args) -> let! tid = Pickler.useTypeDef ctor.DeclaringEntity.Value targs let! tids = args |> List.mapS (fun a -> Pickler.useType a.Type) - do! Pickler.write 26uy - do! Pickler.write tid - do! Pickler.write tids + do! Pickler.writeByte 26uy + do! Pickler.writeInt tid + do! Pickler.writeIntArray tids for a in args do do! serializeS a | BasicPatterns.NewRecord(typ, args) -> let! tid = Pickler.useType typ - do! Pickler.write 27uy - do! Pickler.write tid - do! Pickler.write args.Length + do! Pickler.writeByte 27uy + do! Pickler.writeInt tid + do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.NewTuple(typ, args) -> - do! Pickler.write 28uy - do! Pickler.write args.Length + do! Pickler.writeByte 28uy + do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.NewUnionCase(typ, case, args) -> let! tid = Pickler.useType typ - do! Pickler.write 29uy - do! Pickler.write tid - do! Pickler.write case.Name - do! Pickler.write args.Length + do! Pickler.writeByte 29uy + do! Pickler.writeInt tid + do! Pickler.writeString case.Name + do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.Quote(e) -> - do! Pickler.write 30uy + do! Pickler.writeByte 30uy do! serializeS e | BasicPatterns.Sequential(l, r) -> - do! Pickler.write 31uy + do! Pickler.writeByte 31uy do! serializeS l do! serializeS r | BasicPatterns.TupleGet(_typ, i, target) -> - do! Pickler.write 32uy - do! Pickler.write i + do! Pickler.writeByte 32uy + do! Pickler.writeInt i do! serializeS target | BasicPatterns.TypeTest(typ, target) -> let! tid = Pickler.useType typ - do! Pickler.write 33uy - do! Pickler.write tid + do! Pickler.writeByte 33uy + do! Pickler.writeInt tid do! serializeS target | BasicPatterns.UnionCaseTag(e, t) -> @@ -449,14 +487,14 @@ and serializeS (expr : FSharpExpr) = let! var = Pickler.tryGetVar v match var with | Some var -> - do! Pickler.write 36uy - do! Pickler.write var + do! Pickler.writeByte 36uy + do! Pickler.writeInt var do! serializeS value | None -> // code 37 return failwith "bad" | BasicPatterns.WhileLoop(guard, body) -> - do! Pickler.write 38uy + do! Pickler.writeByte 38uy do! serializeS guard do! serializeS body @@ -469,8 +507,8 @@ and serializeS (expr : FSharpExpr) = | [] -> return! serializeS body | (v,e) :: ls -> let! var = Pickler.newVar v - do! Pickler.write 4uy - do! Pickler.write var + do! Pickler.writeByte 4uy + do! Pickler.writeInt var do! serializeS e do! wrap ls } @@ -557,35 +595,35 @@ and serializeS (expr : FSharpExpr) = let! aids = m.CurriedParameterGroups |> Seq.concat |> Seq.toList |> List.mapS (fun p -> Pickler.useType p.Type) match target with | Some target -> - do! Pickler.write 5uy - do! Pickler.write tid - do! Pickler.write m.CompiledName - do! Pickler.write mpars - do! Pickler.write margs - do! Pickler.write aids - do! Pickler.write rid - do! Pickler.write args.Length + do! Pickler.writeByte 5uy + do! Pickler.writeInt tid + do! Pickler.writeString m.CompiledName + do! Pickler.writeStringArray mpars + do! Pickler.writeIntArray margs + do! Pickler.writeIntArray aids + do! Pickler.writeInt rid + do! Pickler.writeInt args.Length do! serializeS target for a in args do do! serializeS a | _ -> - do! Pickler.write 6uy - do! Pickler.write tid - do! Pickler.write m.CompiledName - do! Pickler.write mpars - do! Pickler.write margs - do! Pickler.write aids - do! Pickler.write rid - do! Pickler.write args.Length + do! Pickler.writeByte 6uy + do! Pickler.writeInt tid + do! Pickler.writeString m.CompiledName + do! Pickler.writeStringArray mpars + do! Pickler.writeIntArray margs + do! Pickler.writeIntArray aids + do! Pickler.writeInt rid + do! Pickler.writeInt args.Length for a in args do do! serializeS a | _ -> - do! Pickler.write 255uy - do! Pickler.write (sprintf "BAD EXPRESSION: %A" expr) + do! Pickler.writeByte 255uy + do! Pickler.writeString (sprintf "BAD EXPRESSION: %A" expr) } type VarData = @@ -604,11 +642,10 @@ type ExprData = let serialize (expr : FSharpExpr) = let s = serializeS expr - use stream = new System.IO.MemoryStream() - use w = new System.IO.BinaryWriter(stream) + let w = BinaryWriter() let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = [] } - w.Flush() - let data = stream.ToArray() + + let data = w.ToByteArray() let variables = s.variables |> List.sortBy snd diff --git a/src/fable-standalone/src/Fable.Standalone.fsproj b/src/fable-standalone/src/Fable.Standalone.fsproj index df72aa5f6a..142341b1a6 100644 --- a/src/fable-standalone/src/Fable.Standalone.fsproj +++ b/src/fable-standalone/src/Fable.Standalone.fsproj @@ -26,6 +26,7 @@ + From a3b129c4de7a2009543db46c8d36f7592fda24f6 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 13:23:11 +0200 Subject: [PATCH 21/38] Tests: re-enabled DateTimeOffset test --- tests/Main/DateTimeOffsetTests.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Main/DateTimeOffsetTests.fs b/tests/Main/DateTimeOffsetTests.fs index cf69f6be4a..f6c661bdc5 100644 --- a/tests/Main/DateTimeOffsetTests.fs +++ b/tests/Main/DateTimeOffsetTests.fs @@ -48,11 +48,11 @@ let tests = // dof.Offset |> equal (TimeSpan.FromHours(3.)) // dof.ToString("HH:mm") |> equal "17:30" - // testCase "DateTimeOffset from Year 1 to 99 works" <| fun () -> - // let date = DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.Zero) - // date.Year |> equal 1 - // let date = DateTimeOffset(99, 1, 1, 0, 0, 0, TimeSpan.Zero) - // date.Year |> equal 99 + testCase "DateTimeOffset from Year 1 to 99 works" <| fun () -> + let date = DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.Zero) + date.Year |> equal 1 + let date = DateTimeOffset(99, 1, 1, 0, 0, 0, TimeSpan.Zero) + date.Year |> equal 99 // TODO: These two tests give different values for .NET and JS because DateTimeOffset // becomes as a plain JS Date object, so I'm just checking the fields get translated From 183614d07d9ae76085e0d2625fa06e0fe31c7706 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 13:26:14 +0200 Subject: [PATCH 22/38] Fable.Transforms: re-added DisableImplicitFSharpCoreReference --- src/Fable.Transforms/Fable.Transforms.fsproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Fable.Transforms/Fable.Transforms.fsproj b/src/Fable.Transforms/Fable.Transforms.fsproj index 4a9af0055c..1743f290c1 100644 --- a/src/Fable.Transforms/Fable.Transforms.fsproj +++ b/src/Fable.Transforms/Fable.Transforms.fsproj @@ -1,6 +1,8 @@ + netstandard2.0 + true From 073396bf49aeb20c9ff3868e7005565595de8557 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 15:12:45 +0200 Subject: [PATCH 23/38] added SourceLocations for Quotations and improved error handling --- src/Fable.Transforms/AST/AST.Fable.fs | 9 ++-- src/Fable.Transforms/FSharp2Fable.fs | 42 +++++++++++++---- src/Fable.Transforms/Fable2Babel.fs | 4 +- src/Fable.Transforms/FableTransforms.fs | 4 +- src/Fable.Transforms/QuotationPickler.fs | 60 ++++++++++++------------ 5 files changed, 72 insertions(+), 47 deletions(-) diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index a125f44be7..1fd6fe00b5 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -354,12 +354,12 @@ type Expr = | TryCatch of body: Expr * catch: (Ident * Expr) option * finalizer: Expr option * range: SourceLocation option | IfThenElse of guardExpr: Expr * thenExpr: Expr * elseExpr: Expr * range: SourceLocation option - | Quote of typed : bool * data : ExprData + | Quote of typed : bool * data : ExprData * range : SourceLocation option member this.Type = match this with - | Quote(true, value) -> Expr(Some value.typ) - | Quote(false, _) -> Expr None + | Quote(true, value, _) -> Expr(Some value.typ) + | Quote(false, _, _) -> Expr None | Test _ -> Boolean | Value(kind,_) -> kind.Type | IdentExpr id -> id.Type @@ -377,11 +377,12 @@ type Expr = match this with | Import _ | DelayedResolution _ | ObjectExpr _ | Sequential _ | Let _ - | DecisionTree _ | DecisionTreeSuccess _ | Quote _ -> None + | DecisionTree _ | DecisionTreeSuccess _ -> None | Function(_,e,_) | TypeCast(e,_) -> e.Range | IdentExpr id -> id.Range + | Quote(_,_,r) | Value(_,r) | IfThenElse(_,_,_,r) | TryCatch(_,_,_,r) | Debugger r | Test(_,_,r) | Operation(_,_,r) | Get(_,_,_,r) | Throw(_,_,r) | Set(_,_,_,r) | Loop(_,r) -> r diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 5acee9f078..fd71f33367 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -893,10 +893,39 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = | BasicPatterns.Quote expr -> + + + //let! dummy = transformExpr com ctx expr - let data = QuotationPickler.serialize expr + let data = QuotationPickler.serialize com ctx expr //data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) + let! values = + data.values |> Array.toList |> trampolineListMap (fun v -> + trampoline { + let! value = + trampoline { + if isInline v then + match ctx.ScopeInlineValues |> List.tryFind (fun (vi,_) -> obj.Equals(vi, v)) with + | Some (_,fsExpr) -> + + return! transformExpr com ctx fsExpr + | None -> + return "Cannot resolve locally inlined value: " + v.DisplayName + |> addErrorAndReturnNull com ctx.InlinePath None + else + return makeValueFrom com ctx None v + } + + + return { + Fable.ValueData.name = v.DisplayName + Fable.ValueData.typ = makeType com ctx.GenericArgs v.FullType + Fable.ValueData.expr = value + } + } + ) + let fableData = { Fable.ExprData.typ = makeType com ctx.GenericArgs data.typ @@ -908,14 +937,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = Fable.VarData.isMutable = v.isMutable } ) - Fable.values = - data.values |> Array.map (fun v -> - { - Fable.ValueData.name = v.DisplayName - Fable.ValueData.typ = makeType com ctx.GenericArgs v.FullType - Fable.ValueData.expr = makeValueFrom com ctx None v - } - ) + Fable.values = List.toArray values Fable.ExprData.types = data.types |> Array.map (fun d -> @@ -950,7 +972,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = } - return Fable.Quote(false, fableData) + return Fable.Quote(false, fableData, makeRangeFrom fsExpr) // return "Quotes are not currently supported by Fable" // |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index f715e0dde6..e0d2566596 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -1345,7 +1345,7 @@ module Util = | Fable.Debugger _ | Fable.Throw _ | Fable.Loop _ | Fable.TryCatch _ -> iife com ctx expr :> Expression - | Fable.Quote(_,data) -> + | Fable.Quote(_,data, r) -> let obj (values : list) = values |> List.toArray |> Array.map (fun (n,v) -> U3.Case1 (ObjectProperty(StringLiteral n, v))) |> ObjectExpression :> Expression let values = @@ -1392,7 +1392,7 @@ module Util = // let arrName = getTypedArrayName com NumberKind.UInt8 // let expr = NewExpression(Identifier arrName, [| data.data |> Array.map (fun v -> NumericLiteral (float v) :> Expression) |> ArrayExpression |]) - coreLibCall com ctx None "ExprUtils" "deserialize" [| + coreLibCall com ctx r "ExprUtils" "deserialize" [| ArrayExpression values ArrayExpression vars ArrayExpression types diff --git a/src/Fable.Transforms/FableTransforms.fs b/src/Fable.Transforms/FableTransforms.fs index 8d1ab25f41..199df608bf 100644 --- a/src/Fable.Transforms/FableTransforms.fs +++ b/src/Fable.Transforms/FableTransforms.fs @@ -7,7 +7,7 @@ open FSharp.Compiler.SourceCodeServices // TODO: Use trampoline here? let visit f e = match e with - | Quote(b, e) -> Quote(b, e) + | Quote(b, e, r) -> Quote(b, { e with values = e.values |> Array.map (fun v -> { v with expr = f v.expr }) }, r) | IdentExpr _ | Debugger _ -> e | TypeCast(e, t) -> TypeCast(f e, t) | Import(e1, e2, kind, t, r) -> Import(f e1, f e2, kind, t, r) @@ -110,7 +110,7 @@ let rec visitFromOutsideIn (f: Expr->Expr option) e = visit (visitFromOutsideIn f) e let getSubExpressions = function - | Quote _ -> [] + | Quote(_, data, _) -> data.values |> Array.toList |> List.map (fun v -> v.expr) | IdentExpr _ | Debugger _ -> [] | TypeCast(e,_) -> [e] | Import(e1,e2,_,_,_) -> [e1;e2] diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index 80d049f926..8ecf2ccb0e 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -1,11 +1,14 @@ module Fable.Transforms.FSharp2Fable.QuotationPickler +open Fable.Transforms open FSharp.Compiler.SourceCodeServices +open Fable.Transforms.FSharp2Fable.Helpers type MemberDescription = | Member of FSharpMemberOrFunctionOrValue * list * list | UnionCase of FSharpUnionCase * list + type BinaryWriter() = static let nextPowerOfTwo (v : int) = @@ -72,6 +75,10 @@ type PicklerState = cases : list * FSharpExpr>> + com : IFableCompiler + ctx : Context + err : bool + writer : BinaryWriter } @@ -246,7 +253,19 @@ module Pickler = match s.cases with | h :: _ -> h.[i] | _ -> failwith "invalid case" - ) + ) + + + let inline addError r msg = + { run = fun s -> + addError s.com s.ctx.InlinePath r msg + { s with err = true }, () + } + + let inline addWarning r msg = + State.get |> State.map (fun s -> + addWarning s.com s.ctx.InlinePath r msg + ) let rec propertyGetS (tid : int) (target : Option) (name : string) (index : list) (ret : int) = state { match target with @@ -419,7 +438,7 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewAnonRecord(typ, fields) -> // code 23 - return failwith "bad" + do! Pickler.addError (makeRangeFrom expr) "anonymous records not supported atm." | BasicPatterns.NewArray(elementType, args) -> let! tid = Pickler.useType elementType @@ -430,7 +449,7 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewDelegate _ -> // code 25 - return failwith "bad" + do! Pickler.addError (makeRangeFrom expr) "delegates not supported atm." | BasicPatterns.NewObject(ctor, targs, args) -> let! tid = Pickler.useTypeDef ctor.DeclaringEntity.Value targs @@ -479,10 +498,12 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.UnionCaseTag(e, t) -> // code 34 - return failwith "bad" + do! Pickler.addError (makeRangeFrom expr) "UnionCaseTags not supported atm." + | BasicPatterns.UnionCaseSet(target, typ, case, prop, value) -> // code 35 - return failwith "bad" + do! Pickler.addError (makeRangeFrom expr) "UnionCaseSet not supported atm." + | BasicPatterns.ValueSet(v, value) -> let! var = Pickler.tryGetVar v match var with @@ -492,7 +513,7 @@ and serializeS (expr : FSharpExpr) = do! serializeS value | None -> // code 37 - return failwith "bad" + do! Pickler.addError (makeRangeFrom expr) "static property sets not supported atm." | BasicPatterns.WhileLoop(guard, body) -> do! Pickler.writeByte 38uy do! serializeS guard @@ -531,25 +552,6 @@ and serializeS (expr : FSharpExpr) = let! ret = Pickler.useType m.ReturnParameter.Type do! propertyGetS tid target m.CompiledName [] ret - //elif m.IsExtensionMember then - - // if m.IsPropertyGetterMethod then - // let name = - // let name = m.LogicalName - // if name.StartsWith "get_" then name.Substring(4) - // else name - - // let args = - // match args with - // | unitVal :: rest when Helpers.isUnit unitVal.Type -> rest - // | args -> args - // let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs - // let! ret = Pickler.useType m.ReturnParameter.Type - // do! propertyGetS tid target name args ret - // else - - // failwithf "%A %A %A %A %A %A" m.DisplayName m.CompiledName m.LogicalName m.FullName m.IsPropertyGetterMethod m.IsPropertySetterMethod - elif not m.IsExtensionMember && m.IsPropertyGetterMethod then let name = let name = m.CompiledName @@ -578,7 +580,7 @@ and serializeS (expr : FSharpExpr) = let idx, value = match args with - | [] -> failwith "bad" + | [] -> failwith "unreachable" | _ -> let value = List.last args let idx = List.take (args.Length - 1) args @@ -587,7 +589,6 @@ and serializeS (expr : FSharpExpr) = let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs do! propertySetS tid target name idx value else - //let! mem = Pickler.useMember m targs margs let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs let! rid = Pickler.useType m.ReturnParameter.Type let! margs = margs |> List.mapS Pickler.useType @@ -640,10 +641,10 @@ type ExprData = data : byte[] } -let serialize (expr : FSharpExpr) = +let serialize (com : IFableCompiler) (ctx : Context) (expr : FSharpExpr) = let s = serializeS expr let w = BinaryWriter() - let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = [] } + let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = []; com = com; ctx = ctx; err = false } let data = w.ToByteArray() let variables = @@ -669,6 +670,7 @@ let serialize (expr : FSharpExpr) = + From 951eaa26c6cecd74a9bb1d2120106a0dd8e6939e Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 16:24:06 +0200 Subject: [PATCH 24/38] fixed quotation error messages --- src/Fable.Transforms/QuotationPickler.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index 8ecf2ccb0e..ca1405e9de 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -438,7 +438,7 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewAnonRecord(typ, fields) -> // code 23 - do! Pickler.addError (makeRangeFrom expr) "anonymous records not supported atm." + do! Pickler.addError (makeRangeFrom expr) "anonymous records not supported in quotations atm." | BasicPatterns.NewArray(elementType, args) -> let! tid = Pickler.useType elementType @@ -449,7 +449,7 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewDelegate _ -> // code 25 - do! Pickler.addError (makeRangeFrom expr) "delegates not supported atm." + do! Pickler.addError (makeRangeFrom expr) "delegates not supported in quotations atm." | BasicPatterns.NewObject(ctor, targs, args) -> let! tid = Pickler.useTypeDef ctor.DeclaringEntity.Value targs @@ -498,11 +498,11 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.UnionCaseTag(e, t) -> // code 34 - do! Pickler.addError (makeRangeFrom expr) "UnionCaseTags not supported atm." + do! Pickler.addError (makeRangeFrom expr) "UnionCaseTags not supported in quotations atm." | BasicPatterns.UnionCaseSet(target, typ, case, prop, value) -> // code 35 - do! Pickler.addError (makeRangeFrom expr) "UnionCaseSet not supported atm." + do! Pickler.addError (makeRangeFrom expr) "UnionCaseSet not supported in quotations atm." | BasicPatterns.ValueSet(v, value) -> let! var = Pickler.tryGetVar v @@ -513,7 +513,7 @@ and serializeS (expr : FSharpExpr) = do! serializeS value | None -> // code 37 - do! Pickler.addError (makeRangeFrom expr) "static property sets not supported atm." + do! Pickler.addError (makeRangeFrom expr) "static property sets not supported in quotations atm." | BasicPatterns.WhileLoop(guard, body) -> do! Pickler.writeByte 38uy do! serializeS guard From 4ed71751834cdbd1f3132eadacb06103a868447b Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 19:08:00 +0200 Subject: [PATCH 25/38] Fable.Library: added preprocessor defines again --- src/fable-library/Fable.Library.fsproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fable-library/Fable.Library.fsproj b/src/fable-library/Fable.Library.fsproj index bd099f485d..cc347773f4 100644 --- a/src/fable-library/Fable.Library.fsproj +++ b/src/fable-library/Fable.Library.fsproj @@ -1,6 +1,8 @@ netstandard2.0 + $(DefineConstants);FABLE_COMPILER + $(DefineConstants);FX_NO_BIGINT From bcddad5f33ccbda286dcf078b57c315142340e8b Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Tue, 23 Apr 2019 19:21:17 +0200 Subject: [PATCH 26/38] build: increased max heap size --- build.fsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.fsx b/build.fsx index e6dd36accb..311bfedab4 100644 --- a/build.fsx +++ b/build.fsx @@ -57,7 +57,7 @@ let buildSplitter projectDir = buildSplitterWithArgs projectDir "" let buildWebpack projectDir = - run ("npx webpack --config " + (projectDir "webpack.config.js")) + run ("npx --node-arg \"--max_old_space_size=4096\" webpack --config " + (projectDir "webpack.config.js")) let buildLibrary() = cleanDirs ["build/fable-library"] From efd5b739a775b406647b07df1d1211c065cb793f Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Wed, 24 Apr 2019 11:18:45 +0200 Subject: [PATCH 27/38] Quotation cleanup (optional debug ranges for Expr) --- src/Fable.Transforms/QuotationPickler.fs | 304 ++--- src/fable-library/ExprUtils.fs | 106 +- src/fable-library/Quotations.fs | 1345 +++------------------- 3 files changed, 381 insertions(+), 1374 deletions(-) diff --git a/src/Fable.Transforms/QuotationPickler.fs b/src/Fable.Transforms/QuotationPickler.fs index ca1405e9de..8cb5ce410e 100644 --- a/src/Fable.Transforms/QuotationPickler.fs +++ b/src/Fable.Transforms/QuotationPickler.fs @@ -75,6 +75,7 @@ type PicklerState = cases : list * FSharpExpr>> + debug : bool com : IFableCompiler ctx : Context err : bool @@ -82,48 +83,38 @@ type PicklerState = writer : BinaryWriter } -type State<'s, 'a> = { run : 's -> 's * 'a } +type State<'s, 'a> = ref<'s> -> 'a + +//type State<'s, 'a> = { run : 's -> 's * 'a } module State = - let inline get<'s, 'a> = { run = fun s -> s, s } - let inline put (s : 's) = { run = fun _ -> s, () } - let inline modify (f : 's -> 's) = { run = fun s -> f s, () } - - let inline map (f : 'a -> 'b) (m : State<'s, 'a>) = - { run = fun s -> - let s, a = m.run s - s, f a - } - let inline bind (f : 'a -> State<'s, 'b>) (m : State<'s, 'a>) = - { run = fun s -> - let (s,a) = m.run s - (f a).run s - } + let inline get<'s> : State<'s, 's> = fun s -> !s + let inline put (n : 's) : State<'s, unit> = fun s -> s := n + let inline modify (f : 's -> 's) : State<'s, unit> = fun s -> s := f !s + + let inline map (f : 'a -> 'b) (m : State<'s, 'a>) : State<'s, 'b> = + fun s -> f (m s) + let inline bind (f : 'a -> State<'s, 'b>) (m : State<'s, 'a>) : State<'s, 'b> = + fun s -> f (m s) s - let inline value (v : 'a) = { run = fun s -> s, v } + let inline value (v : 'a) : State<'s, 'a> = fun _ -> v type StateBuilder() = member inline x.Bind(m : State<'s, 'a>, f : 'a -> State<'s, 'b>) = bind f m member inline x.Return v = value v member inline x.ReturnFrom(s : State<'s, 'a>) = s member inline x.Zero() = value () - member inline x.Delay (f : unit -> State<'s, 'a>) = { run = fun s -> f().run s } + member inline x.Delay (f : unit -> State<'s, 'a>) = fun s -> f () s member inline x.Combine(l : State<'s, unit>, r : State<'s, 'a>) = l |> bind (fun () -> r) member inline x.For(seq : seq<'a>, action : 'a -> State<'s, unit>) = - { run = fun s -> - let mutable s = s + fun s -> for e in seq do - let (s1, ()) = (action e).run s - s <- s1 - s, () - } + action e s + member inline x.While(guard : unit -> bool, body : State<'s, unit>) = - { run = fun s -> - let mutable s = s + fun s -> while guard() do - let s1, () = body.run s - s <- s1 - s, () - } + body s + let state = State.StateBuilder() @@ -135,13 +126,15 @@ module List = f h |> State.bind (fun h -> mapS f t |> State.map (fun t -> h :: t)) module Pickler = - let inline newVar (l : FSharpMemberOrFunctionOrValue) = - { run = fun s -> - { s with - varId = s.varId + 1 - variables = (l, s.varId) :: s.variables - }, s.varId - } + let inline newVar (l : FSharpMemberOrFunctionOrValue) : State = + fun (s : ref) -> + let o = !s + s := + { o with + varId = o.varId + 1 + variables = (l, o.varId) :: o.variables + } + o.varId let inline tryGetVar (l : FSharpMemberOrFunctionOrValue) = State.get |> State.map (fun s -> @@ -151,78 +144,84 @@ module Pickler = let inline getVar (l : FSharpMemberOrFunctionOrValue) = tryGetVar l |> State.map Option.get - let inline useValue (l : FSharpMemberOrFunctionOrValue) = - { run = fun s -> - let res = s.values |> List.tryPick (fun (v,i) -> if v = l then Some i else None) + let inline useValue (l : FSharpMemberOrFunctionOrValue) : State = + fun s -> + let o = !s + let res = o.values |> List.tryPick (fun (v,i) -> if v = l then Some i else None) match res with | Some res -> - s, res + res | None -> - let id = s.valueId - { s with valueId = id + 1; values = (l, id) :: s.values }, id - } - let inline useType (t : FSharpType) = - { run = fun s -> - let res = s.types |> List.tryPick (function (Choice1Of2 v,i) when v = t -> Some i | _ -> None) + let id = o.valueId + s := { o with valueId = id + 1; values = (l, id) :: o.values } + id + let inline useType (t : FSharpType) : State = + fun s -> + let o = !s + let res = o.types |> List.tryPick (function (Choice1Of2 v,i) when v = t -> Some i | _ -> None) match res with | Some res -> - s, res + res | None -> - let id = s.typeId - { s with typeId = id + 1; types = (Choice1Of2 t, id) :: s.types }, id - } - let inline useTypeDef (t : FSharpEntity) (targs : list) = - { run = fun s -> - let res = s.types |> List.tryPick (function (Choice2Of2(v,ta),i) when v = t && ta = targs -> Some i | _ -> None) + let id = o.typeId + s := { o with typeId = id + 1; types = (Choice1Of2 t, id) :: o.types } + id + let inline useTypeDef (t : FSharpEntity) (targs : list) : State= + fun s -> + let o = !s + let res = o.types |> List.tryPick (function (Choice2Of2(v,ta),i) when v = t && ta = targs -> Some i | _ -> None) match res with | Some res -> - s, res + res | None -> - let id = s.typeId - { s with typeId = id + 1; types = (Choice2Of2(t, targs), id) :: s.types }, id - } - let inline useMember (mem : FSharpMemberOrFunctionOrValue) (targs : list) (margs : list) = - { run = fun s -> - let res = s.members |> List.tryPick (function (Member(v,t,m),i) when v = mem && t = targs && m = margs -> Some i | _ -> None) + let id = o.typeId + s := { o with typeId = id + 1; types = (Choice2Of2(t, targs), id) :: o.types } + id + let inline useMember (mem : FSharpMemberOrFunctionOrValue) (targs : list) (margs : list) : State = + fun s -> + let o = !s + let res = o.members |> List.tryPick (function (Member(v,t,m),i) when v = mem && t = targs && m = margs -> Some i | _ -> None) match res with | Some res -> - s, res + res | None -> - let id = s.memberId - { s with memberId = id + 1; members = (Member (mem, targs, margs), id) :: s.members }, id - } - let inline useUnionCase (case : FSharpUnionCase) (targs : list) = - { run = fun s -> - let res = s.members |> List.tryPick (function (UnionCase(c,t),i) when c = case && t = targs -> Some i | _ -> None) + let id = o.memberId + s := { o with memberId = id + 1; members = (Member (mem, targs, margs), id) :: o.members } + id + + let inline useUnionCase (case : FSharpUnionCase) (targs : list) : State = + fun s -> + let o = !s + let res = o.members |> List.tryPick (function (UnionCase(c,t),i) when c = case && t = targs -> Some i | _ -> None) match res with | Some res -> - s, res + res | None -> - let id = s.memberId - { s with memberId = id + 1; members = (UnionCase(case,targs), id) :: s.members }, id - } + let id = o.memberId + s := { o with memberId = id + 1; members = (UnionCase(case,targs), id) :: o.members } + id + - let inline useLiteral (v : obj) (t : FSharpType) = - { run = fun s -> - let res = s.literals |> List.tryPick (fun (vi,ti,i) -> if vi = v && ti = t then Some i else None) + let inline useLiteral (v : obj) (t : FSharpType) : State = + fun s -> + let o = !s + let res = o.literals |> List.tryPick (fun (vi,ti,i) -> if vi = v && ti = t then Some i else None) match res with | Some res -> - s, res + res | None -> - let id = s.literalId - { s with literalId = id + 1; literals = (v, t, id) :: s.literals }, id - } - - let writeByte (b : byte) = { run = fun s -> s.writer.Write b; s, () } - let writeInt (b : int) = { run = fun s -> s.writer.Write b; s, () } + let id = o.literalId + s := { o with literalId = id + 1; literals = (v, t, id) :: o.literals } + id + //let writeByte (b : byte) = fun (s : ref) -> s.Value.writer.Write b + let writeInt (b : int) = fun (s : ref) -> s.Value.writer.Write b let writeString (v : string) = - { run = fun s -> + fun (s : ref) -> let bytes = System.Text.Encoding.UTF8.GetBytes v - s.writer.Write(bytes.Length) - s.writer.Write bytes - s, () - } + s.Value.writer.Write(bytes.Length) + s.Value.writer.Write bytes + let writeStringArray (v : string[]) = state { @@ -232,12 +231,29 @@ module Pickler = } let writeIntArray (vs : seq) = - { run = fun s -> + fun (s : ref) -> let vs = Seq.toArray vs - s.writer.Write vs.Length - for v in vs do s.writer.Write v - s, () - } + s.Value.writer.Write vs.Length + for v in vs do s.Value.writer.Write v + + let writeOpCode (range : Option) (code : byte)= + fun (s : ref) -> + let s = !s + if s.debug then + match range with + | Some l -> + s.writer.Write (128uy + code) + s.writer.Write(l.start.line) + s.writer.Write(l.start.column) + s.writer.Write(l.``end``.line) + s.writer.Write(l.``end``.column) + | None -> + s.writer.Write (code) + else + s.writer.Write (code) + + + let inline pushCases (cs : array * FSharpExpr>) = State.modify (fun s -> { s with cases = cs :: s.cases }) @@ -257,20 +273,20 @@ module Pickler = let inline addError r msg = - { run = fun s -> - addError s.com s.ctx.InlinePath r msg - { s with err = true }, () - } + fun (s : ref) -> + addError s.Value.com s.Value.ctx.InlinePath r msg + s := { !s with err = true } + let inline addWarning r msg = State.get |> State.map (fun s -> addWarning s.com s.ctx.InlinePath r msg ) -let rec propertyGetS (tid : int) (target : Option) (name : string) (index : list) (ret : int) = +let rec propertyGetS (loc : Option) (tid : int) (target : Option) (name : string) (index : list) (ret : int) = state { match target with | Some target -> - do! Pickler.writeByte 18uy + do! Pickler.writeOpCode loc 18uy do! Pickler.writeInt tid do! Pickler.writeString name do! Pickler.writeInt index.Length @@ -278,7 +294,7 @@ let rec propertyGetS (tid : int) (target : Option) (name : string) ( do! Pickler.writeInt ret do! serializeS target | None -> - do! Pickler.writeByte 19uy + do! Pickler.writeOpCode loc 19uy do! Pickler.writeInt tid do! Pickler.writeString name do! Pickler.writeInt index.Length @@ -286,12 +302,12 @@ let rec propertyGetS (tid : int) (target : Option) (name : string) ( do! Pickler.writeInt ret } -and propertySetS (tid : int) (target : Option) (name : string) (index : list) (value : FSharpExpr) = +and propertySetS (loc : Option) (tid : int) (target : Option) (name : string) (index : list) (value : FSharpExpr) = state { let! ret = Pickler.useType value.Type match target with | Some target -> - do! Pickler.writeByte 20uy + do! Pickler.writeOpCode loc 20uy do! Pickler.writeInt tid do! Pickler.writeString name do! Pickler.writeInt index.Length @@ -300,7 +316,7 @@ and propertySetS (tid : int) (target : Option) (name : string) (inde do! serializeS target do! serializeS value | None -> - do! Pickler.writeByte 21uy + do! Pickler.writeOpCode loc 21uy do! Pickler.writeInt tid do! Pickler.writeString name do! Pickler.writeInt index.Length @@ -311,27 +327,28 @@ and propertySetS (tid : int) (target : Option) (name : string) (inde and serializeS (expr : FSharpExpr) = state { + let loc = Helpers.makeRangeFrom expr match expr with | BasicPatterns.Lambda(v, b) -> let! var = Pickler.newVar v - do! Pickler.writeByte 1uy + do! Pickler.writeOpCode loc 1uy do! Pickler.writeInt var return! serializeS b | BasicPatterns.Value v -> match! Pickler.tryGetVar v with | Some var -> - do! Pickler.writeByte 2uy + do! Pickler.writeOpCode loc 2uy do! Pickler.writeInt var | None -> let! var = Pickler.useValue v - do! Pickler.writeByte 3uy + do! Pickler.writeOpCode loc 3uy do! Pickler.writeInt var | BasicPatterns.Let((v, e), b) -> let! var = Pickler.newVar v - do! Pickler.writeByte 4uy + do! Pickler.writeOpCode loc 4uy do! Pickler.writeInt var do! serializeS e do! serializeS b @@ -339,49 +356,49 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.FSharpFieldGet(target, typ, field) -> let! tid = Pickler.useType typ let! ret = Pickler.useType field.FieldType - do! propertyGetS tid target field.Name [] ret + do! propertyGetS loc tid target field.Name [] ret | BasicPatterns.FSharpFieldSet(target, typ, field, value) -> let! tid = Pickler.useType typ - do! propertySetS tid target field.Name [] value + do! propertySetS loc tid target field.Name [] value | BasicPatterns.AddressOf e -> - do! Pickler.writeByte 7uy + do! Pickler.writeOpCode loc 7uy do! serializeS e | BasicPatterns.AddressSet(v, e) -> - do! Pickler.writeByte 8uy + do! Pickler.writeOpCode loc 8uy do! serializeS v do! serializeS e | BasicPatterns.AnonRecordGet(e, t, i) -> let fieldName = t.AnonRecordTypeDetails.SortedFieldNames.[i] let! typ = Pickler.useType t - do! Pickler.writeByte 9uy + do! Pickler.writeOpCode loc 9uy do! Pickler.writeInt typ do! Pickler.writeString fieldName do! serializeS e | BasicPatterns.Application(e, ts, args) -> - do! Pickler.writeByte 10uy + do! Pickler.writeOpCode loc 10uy do! serializeS e do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.Const(o, t) -> - do! Pickler.writeByte 11uy + do! Pickler.writeOpCode loc 11uy let! vid = Pickler.useLiteral o t do! Pickler.writeInt vid | BasicPatterns.IfThenElse(c, i, e) -> - do! Pickler.writeByte 12uy + do! Pickler.writeOpCode loc 12uy do! serializeS c do! serializeS i do! serializeS e | BasicPatterns.UnionCaseTest(expr, typ, case) -> - do! Pickler.writeByte 13uy + do! Pickler.writeOpCode loc 13uy let! tid = Pickler.useType typ do! Pickler.writeInt tid do! Pickler.writeString case.CompiledName @@ -391,7 +408,7 @@ and serializeS (expr : FSharpExpr) = let index = case.UnionCaseFields |> Seq.findIndex (fun pi -> pi = prop) let! tid = Pickler.useType typ - do! Pickler.writeByte 14uy + do! Pickler.writeOpCode loc 14uy do! Pickler.writeInt tid do! Pickler.writeString case.CompiledName do! Pickler.writeInt index @@ -399,18 +416,18 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.Coerce(t, e) -> let! tid = Pickler.useType t - do! Pickler.writeByte 15uy + do! Pickler.writeOpCode loc 15uy do! Pickler.writeInt tid do! serializeS e | BasicPatterns.DefaultValue t -> let! tid = Pickler.useType t - do! Pickler.writeByte 16uy + do! Pickler.writeOpCode loc 16uy do! Pickler.writeInt tid | BasicPatterns.FastIntegerForLoop(s, e, BasicPatterns.Lambda(v, b), true) -> let! vid = Pickler.newVar v - do! Pickler.writeByte 17uy + do! Pickler.writeOpCode loc 17uy do! Pickler.writeInt vid do! serializeS s do! serializeS e @@ -421,14 +438,14 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.ILFieldGet(target, typ, field) -> let! tid = Pickler.useType typ let! ret = Pickler.useType expr.Type - do! propertyGetS tid target field [] ret + do! propertyGetS loc tid target field [] ret | BasicPatterns.ILFieldSet(target, typ, field, value) -> let! tid = Pickler.useType typ - do! propertySetS tid target field [] value + do! propertySetS loc tid target field [] value | BasicPatterns.LetRec(vs, b) -> - do! Pickler.writeByte 22uy + do! Pickler.writeOpCode loc 22uy do! Pickler.writeInt vs.Length for (v, e) in vs do let! vid = Pickler.newVar v @@ -442,7 +459,7 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewArray(elementType, args) -> let! tid = Pickler.useType elementType - do! Pickler.writeByte 24uy + do! Pickler.writeOpCode loc 24uy do! Pickler.writeInt tid do! Pickler.writeInt args.Length for a in args do do! serializeS a @@ -454,45 +471,45 @@ and serializeS (expr : FSharpExpr) = | BasicPatterns.NewObject(ctor, targs, args) -> let! tid = Pickler.useTypeDef ctor.DeclaringEntity.Value targs let! tids = args |> List.mapS (fun a -> Pickler.useType a.Type) - do! Pickler.writeByte 26uy + do! Pickler.writeOpCode loc 26uy do! Pickler.writeInt tid do! Pickler.writeIntArray tids for a in args do do! serializeS a | BasicPatterns.NewRecord(typ, args) -> let! tid = Pickler.useType typ - do! Pickler.writeByte 27uy + do! Pickler.writeOpCode loc 27uy do! Pickler.writeInt tid do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.NewTuple(typ, args) -> - do! Pickler.writeByte 28uy + do! Pickler.writeOpCode loc 28uy do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.NewUnionCase(typ, case, args) -> let! tid = Pickler.useType typ - do! Pickler.writeByte 29uy + do! Pickler.writeOpCode loc 29uy do! Pickler.writeInt tid do! Pickler.writeString case.Name do! Pickler.writeInt args.Length for a in args do do! serializeS a | BasicPatterns.Quote(e) -> - do! Pickler.writeByte 30uy + do! Pickler.writeOpCode loc 30uy do! serializeS e | BasicPatterns.Sequential(l, r) -> - do! Pickler.writeByte 31uy + do! Pickler.writeOpCode loc 31uy do! serializeS l do! serializeS r | BasicPatterns.TupleGet(_typ, i, target) -> - do! Pickler.writeByte 32uy + do! Pickler.writeOpCode loc 32uy do! Pickler.writeInt i do! serializeS target | BasicPatterns.TypeTest(typ, target) -> let! tid = Pickler.useType typ - do! Pickler.writeByte 33uy + do! Pickler.writeOpCode loc 33uy do! Pickler.writeInt tid do! serializeS target @@ -508,14 +525,14 @@ and serializeS (expr : FSharpExpr) = let! var = Pickler.tryGetVar v match var with | Some var -> - do! Pickler.writeByte 36uy + do! Pickler.writeOpCode loc 36uy do! Pickler.writeInt var do! serializeS value | None -> // code 37 do! Pickler.addError (makeRangeFrom expr) "static property sets not supported in quotations atm." | BasicPatterns.WhileLoop(guard, body) -> - do! Pickler.writeByte 38uy + do! Pickler.writeOpCode loc 38uy do! serializeS guard do! serializeS body @@ -528,7 +545,7 @@ and serializeS (expr : FSharpExpr) = | [] -> return! serializeS body | (v,e) :: ls -> let! var = Pickler.newVar v - do! Pickler.writeByte 4uy + do! Pickler.writeOpCode loc 4uy do! Pickler.writeInt var do! serializeS e do! wrap ls @@ -550,7 +567,7 @@ and serializeS (expr : FSharpExpr) = if m.IsValue && List.isEmpty args && List.isEmpty margs && Option.isSome m.DeclaringEntity && m.DeclaringEntity.Value.IsFSharpModule then let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs let! ret = Pickler.useType m.ReturnParameter.Type - do! propertyGetS tid target m.CompiledName [] ret + do! propertyGetS loc tid target m.CompiledName [] ret elif not m.IsExtensionMember && m.IsPropertyGetterMethod then let name = @@ -565,7 +582,7 @@ and serializeS (expr : FSharpExpr) = let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs let! ret = Pickler.useType m.ReturnParameter.Type - do! propertyGetS tid target name args ret + do! propertyGetS loc tid target name args ret elif not m.IsExtensionMember && m.IsPropertySetterMethod then let name = @@ -587,7 +604,7 @@ and serializeS (expr : FSharpExpr) = idx, value let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs - do! propertySetS tid target name idx value + do! propertySetS loc tid target name idx value else let! tid = Pickler.useTypeDef m.DeclaringEntity.Value targs let! rid = Pickler.useType m.ReturnParameter.Type @@ -596,7 +613,7 @@ and serializeS (expr : FSharpExpr) = let! aids = m.CurriedParameterGroups |> Seq.concat |> Seq.toList |> List.mapS (fun p -> Pickler.useType p.Type) match target with | Some target -> - do! Pickler.writeByte 5uy + do! Pickler.writeOpCode loc 5uy do! Pickler.writeInt tid do! Pickler.writeString m.CompiledName do! Pickler.writeStringArray mpars @@ -610,7 +627,7 @@ and serializeS (expr : FSharpExpr) = do! serializeS a | _ -> - do! Pickler.writeByte 6uy + do! Pickler.writeOpCode loc 6uy do! Pickler.writeInt tid do! Pickler.writeString m.CompiledName do! Pickler.writeStringArray mpars @@ -623,7 +640,7 @@ and serializeS (expr : FSharpExpr) = do! serializeS a | _ -> - do! Pickler.writeByte 255uy + do! Pickler.writeOpCode loc 127uy do! Pickler.writeString (sprintf "BAD EXPRESSION: %A" expr) } @@ -644,8 +661,15 @@ type ExprData = let serialize (com : IFableCompiler) (ctx : Context) (expr : FSharpExpr) = let s = serializeS expr let w = BinaryWriter() - let s, () = s.run { varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = []; com = com; ctx = ctx; err = false } + let state = ref { debug = false; varId = 0; variables = []; valueId = 0; values = []; writer = w; typeId = 0; types = []; memberId = 0; members = []; literalId = 0; literals = []; cases = []; com = com; ctx = ctx; err = false } + Pickler.writeString expr.Range.FileName state + + s state + let s = !state + + + let data = w.ToByteArray() let variables = s.variables diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index 62ca7a96fc..f0b4c13b50 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -27,10 +27,18 @@ type BinaryStream(arr : Uint8Array) = member x.Position = position - member x.ReadByte() = + member x.ReadOpCode() = let value = arr.[position] //view.getUint8(float position) position <- position + 1 - unbox value + let code = unbox value + if code >= 128uy then + let sl = x.ReadInt32() + let sc = x.ReadInt32() + let el = x.ReadInt32() + let ec = x.ReadInt32() + code - 128uy, Some(sl, sc, el, ec) + else + code, None member x.ReadInt32() = let value = view.getInt32(float position, true) @@ -86,6 +94,8 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let values = values |> FSharp.Collections.Array.map (fun v -> Expr.ValueWithName(v.value, v.typ, v.name)) let variables = variables |> FSharp.Collections.Array.map (fun v -> Var(v.name, v.typ, v.isMutable)) + let file = stream.ReadString() + let init (n : int) (f : int -> 'a) = let rec init (i : int) = if i >= n then @@ -97,23 +107,27 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let rec read () = - let tag = stream.ReadByte() + let tag, range = stream.ReadOpCode() + let inline withRange (e : Expr) = + match range with + | Some(sl, sc, el, ec) -> e.WithRange(file, sl, sc, el, ec) + | None -> e match tag with | 1uy -> let vid = stream.ReadInt32() let body = read() - Expr.Lambda(variables.[vid], body) + Expr.Lambda(variables.[vid], body) |> withRange | 2uy -> let vid = stream.ReadInt32() - Expr.Var(variables.[vid]) + Expr.Var(variables.[vid]) |> withRange | 3uy -> let vid = stream.ReadInt32() - values.[vid] + values.[vid] |> withRange | 4uy -> let vid = stream.ReadInt32() let e = read() let b = read() - Expr.Let(variables.[vid], e, b) + Expr.Let(variables.[vid], e, b) |> withRange | 5uy -> let decl = types.[stream.ReadInt32()] @@ -141,10 +155,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let mem = if margs.Length > 0 then mem.MakeGenericMethod margs else mem - Expr.Call(target, mem, args) + Expr.Call(target, mem, args) |> withRange | None -> let mem = createMethod decl name mpars margs dargs ret false - Expr.Call(target, mem, args) + Expr.Call(target, mem, args) |> withRange | 6uy -> let decl = types.[stream.ReadInt32()] @@ -171,19 +185,19 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let mem = if margs.Length > 0 then mem.MakeGenericMethod margs else mem - Expr.Call(mem, args) + Expr.Call(mem, args) |> withRange | None -> let mem = createMethod decl name mpars margs dargs ret true - Expr.Call(mem, args) + Expr.Call(mem, args) |> withRange | 7uy -> let e = read() - Expr.AddressOf(e) + Expr.AddressOf(e) |> withRange | 8uy -> let v = read() let e = read() - Expr.AddressSet(v, e) + Expr.AddressSet(v, e) |> withRange | 9uy -> let tid = stream.ReadInt32() @@ -191,29 +205,29 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let target = read() //let prop = FSharp.Reflection.FSharpType.GetRecordFields target.Type |> FSharp.Collections.Array.find (fun p -> p.Name = name) let prop = createRecordProperty target.Type name types.[tid] - Expr.PropertyGet(target, prop) + Expr.PropertyGet(target, prop) |> withRange | 10uy -> let f = read() let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) - Expr.Applications(f, List.map List.singleton args) + Expr.Applications(f, List.map List.singleton args) |> withRange | 11uy -> let id = stream.ReadInt32() let l = literals.[id] - Expr.Value(l.value, l.typ) + Expr.Value(l.value, l.typ) |> withRange | 12uy -> let c = read() let i = read() let e = read() - Expr.IfThenElse(c, i, e) + Expr.IfThenElse(c, i, e) |> withRange | 13uy -> let typ = types.[stream.ReadInt32()] let name = stream.ReadString() let e = read() let case = FSharp.Reflection.FSharpType.GetUnionCases(e.Type) |> FSharp.Collections.Array.find (fun c -> c.Name = name) - Expr.UnionCaseTest(e, case) + Expr.UnionCaseTest(e, case) |> withRange | 14uy -> let typ = types.[stream.ReadInt32()] @@ -223,22 +237,22 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let case = FSharp.Reflection.FSharpType.GetUnionCases(typ) |> FSharp.Collections.Array.find (fun c -> c.Name = name) let prop = case.GetFields().[index] - Expr.PropertyGet(target, prop) + Expr.PropertyGet(target, prop) |> withRange | 15uy -> let typ = types.[stream.ReadInt32()] let e = read() - Expr.Coerce(e, typ) + Expr.Coerce(e, typ) |> withRange | 16uy -> let typ = types.[stream.ReadInt32()] - Expr.DefaultValue typ + Expr.DefaultValue typ |> withRange | 17uy -> let var = variables.[stream.ReadInt32()] let s = read() let e = read() let b = read() - Expr.ForIntegerRangeLoop(var, s, e, b) + Expr.ForIntegerRangeLoop(var, s, e, b) |> withRange | 18uy -> let typ = types.[stream.ReadInt32()] @@ -251,10 +265,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertyGet(target, prop, idx) + Expr.PropertyGet(target, prop, idx) |> withRange | None -> let prop = createRecordProperty typ name ret - Expr.PropertyGet(target, prop, idx) + Expr.PropertyGet(target, prop, idx) |> withRange | 19uy -> let typ = types.[stream.ReadInt32()] @@ -266,10 +280,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertyGet(prop, idx) + Expr.PropertyGet(prop, idx) |> withRange | None -> let prop = createStaticProperty typ name ret - Expr.PropertyGet(prop, idx) + Expr.PropertyGet(prop, idx) |> withRange | 20uy -> let typ = types.[stream.ReadInt32()] @@ -283,10 +297,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertySet(target, prop, value, idx) + Expr.PropertySet(target, prop, value, idx) |> withRange | None -> let prop = createRecordProperty typ name ret - Expr.PropertySet(target, prop, value, idx) + Expr.PropertySet(target, prop, value, idx) |> withRange | 21uy -> let typ = types.[stream.ReadInt32()] @@ -299,10 +313,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> - Expr.PropertySet(prop, value, idx) + Expr.PropertySet(prop, value, idx) |> withRange | None -> let prop = createStaticProperty typ name ret - Expr.PropertySet(prop, value, idx) + Expr.PropertySet(prop, value, idx) |> withRange | 22uy -> let cnt = stream.ReadInt32() @@ -313,13 +327,13 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty v, e ) let body = read() - Expr.LetRecursive(bindings, body) + Expr.LetRecursive(bindings, body) |> withRange | 24uy -> let typ = types.[stream.ReadInt32()] let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) - Expr.NewArray(typ, args) + Expr.NewArray(typ, args) |> withRange | 26uy -> let typ = types.[stream.ReadInt32()] @@ -335,7 +349,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty match ctor with | Some ctor -> - Expr.NewObject(ctor, args) + Expr.NewObject(ctor, args) |> withRange | _ -> failwith "no ctor found" @@ -343,12 +357,12 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let typ = types.[stream.ReadInt32()] let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) - Expr.NewRecord(typ, args) + Expr.NewRecord(typ, args) |> withRange | 28uy -> let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) - Expr.NewTuple(args) + Expr.NewTuple(args) |> withRange | 29uy -> let typ = types.[stream.ReadInt32()] @@ -357,37 +371,41 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let args = init cnt (fun _ -> read()) // TODO: non existing unions let case = FSharp.Reflection.FSharpType.GetUnionCases(typ) |> FSharp.Collections.Array.find (fun c -> c.Name = name) - Expr.NewUnionCase(case, args) + Expr.NewUnionCase(case, args) |> withRange | 30uy -> let e = read() - Expr.Quote(e) + Expr.Quote(e) |> withRange | 31uy -> let l = read() let r = read() - Expr.Sequential(l, r) + Expr.Sequential(l, r) |> withRange | 32uy -> let i = stream.ReadInt32() let t = read() - Expr.TupleGet(t, i) + Expr.TupleGet(t, i) |> withRange | 33uy -> let typ = types.[stream.ReadInt32()] let target = read() - Expr.TypeTest(target, typ) + Expr.TypeTest(target, typ) |> withRange | 36uy -> let v = variables.[stream.ReadInt32()] let value = read() - Expr.VarSet(v, value) + Expr.VarSet(v, value) |> withRange | 38uy -> let guard = read() let body = read() - Expr.WhileLoop(guard, body) + Expr.WhileLoop(guard, body) |> withRange - | 255uy -> + | 127uy -> let str = stream.ReadString() - failwithf "unsupported expression: %s" str + match range with + | Some (sl, sc, _el, _ec) -> + failwithf "%s [%d, %d]: unsupported expression: %s" file sl sc str + | None -> + failwithf "%s: unsupported expression: %s" file str | _ -> failwithf "invalid expression: %A at %A" tag stream.Position diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 9447ab9e02..417fee940b 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -15,10 +15,6 @@ open Microsoft.FSharp.Core.Printf open Fable.Core #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 @@ -45,14 +41,14 @@ module Helpers = | _ -> 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 inline mkRLinear mk (vs, body) = List.foldBack (fun v acc -> mk(v, acc)) vs body + let inline mkLLinear mk (body, vs) = List.fold (fun acc v -> mk(acc, v)) body vs - let isDelegateType (typ:Type) = + let inline isDelegateType (typ:Type) = typ.FullName.StartsWith "System.Func" - let getDelegateNargs (ty:Type) = + let inline getDelegateNargs (ty:Type) = if ty.FullName.StartsWith "System.Func`" then let ngen = ty.FullName.Substring(12, ty.FullName.Length - 12) |> int ngen - 1 @@ -65,7 +61,7 @@ module Helpers = | null -> nullArg argName | _ -> () - let getTypesFromParamInfos (infos : ParameterInfo[]) = infos |> Array.map (fun pi -> pi.ParameterType) + let inline getTypesFromParamInfos (infos : ParameterInfo[]) = infos |> Array.map (fun pi -> pi.ParameterType) open Helpers @@ -96,13 +92,11 @@ type Var(name: string, typ:Type, ?isMutable: bool) = static member Global(name, typ: Type) = checkNonNull "name" name checkNonNull "typ" typ - lock globals (fun () -> - //let mutable res = Unchecked.defaultof - let ok = globals.ContainsKey((name, typ)) //.TryGetValue((name, typ), &res) - if ok then globals.[(name, typ)] else - let res = Var(name, typ) - globals.[(name, typ)] <- res - res) + let ok = globals.ContainsKey((name, typ)) //.TryGetValue((name, typ), &res) + if ok then globals.[(name, typ)] else + let res = Var(name, typ) + globals.[(name, typ)] <- res + res override v.ToString() = name @@ -195,26 +189,26 @@ and [] eq x.Tree y.Tree | _ -> false member x.GetLayout(long) = - let expr (e:Expr ) = e.GetLayout(long) - let exprs (es:Expr list) = es |> List.map expr - let parens ls = sprintf "(%s)" (String.concat ", " ls) - let pairL l1 l2 = sprintf "(%s, %s)" l1 l2 - let listL ls = sprintf "[%s]" (String.concat ", " ls) - let combTaggedL nm ls = sprintf "%s%s" nm (parens ls) - let combL nm ls = sprintf "%s%s" nm (parens ls) + let inline expr (e:Expr ) = e.GetLayout(long) + let inline exprs (es:Expr list) = es |> List.map expr + let inline parens ls = sprintf "(%s)" (String.concat ", " ls) + let inline pairL l1 l2 = sprintf "(%s, %s)" l1 l2 + let inline listL ls = sprintf "[%s]" (String.concat ", " ls) + let inline combTaggedL nm ls = sprintf "%s%s" nm (parens ls) + let inline combL nm ls = sprintf "%s%s" nm (parens ls) let noneL = "None" - let someL e = sprintf "Some(%s)" (expr e) - let typeL (o: Type) = if long then o.FullName else o.Name - let objL (o: 'T) = sprintf "%A" o - let varL (v:Var) = 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 unionCase.Name) - let minfoL (minfo: MethodInfo) = if long then objL minfo else minfo.Name - let cinfoL (cinfo: ConstructorInfo) = if long then objL cinfo else cinfo.DeclaringType.Name - let pinfoL (pinfo: PropertyInfo) = if long then objL pinfo else pinfo.Name - let finfoL (finfo: FieldInfo) = if long then objL finfo else finfo.Name + let inline someL e = sprintf "Some(%s)" (expr e) + let inline typeL (o: Type) = if long then o.FullName else o.Name + let inline objL (o: 'T) = sprintf "%A" o + let inline varL (v:Var) = v.Name + let inline (|E|) (e: Expr) = e.Tree + let inline (|Lambda|_|) (E x) = match x with LambdaTerm(a, b) -> Some (a, b) | _ -> None + let inline (|IteratedLambda|_|) (e: Expr) = qOneOrMoreRLinear (|Lambda|_|) e + let inline ucaseL (unionCase:UnionCaseInfo) = (if long then objL unionCase else unionCase.Name) + let inline minfoL (minfo: MethodInfo) = if long then objL minfo else minfo.Name + let inline cinfoL (cinfo: ConstructorInfo) = if long then objL cinfo else cinfo.DeclaringType.Name + let inline pinfoL (pinfo: PropertyInfo) = if long then objL pinfo else pinfo.Name + let inline finfoL (finfo: FieldInfo) = if long then objL finfo else finfo.Name let rec (|NLambdas|_|) n (e:Expr) = match e with | _ when n <= 0 -> Some([], e) @@ -295,9 +289,6 @@ and [] [] 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) = @@ -340,12 +331,12 @@ module Patterns = let unitTy = typeof 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 inline 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 inline mkArrayTy (t:Type) = t.MakeArrayType() + let inline mkExprTy (t:Type) = exprTyC.MakeGenericType([| t |]) let rawExprTy = typeof @@ -638,190 +629,121 @@ module Patterns = // 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("{0}/{1}: {2} {3}", threeHoleSR, name, expectedType.FullName, receivedType.FullName)) - - 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) = - if (paramInfos.Length <> args.Length) then invalidArg "args" ("SR.GetString(SR.QincorrectNumArgs)") - List.iter2 - ( fun (p:ParameterInfo) a -> checkTypesWeakSR p.ParameterType (exprType 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 = exprType f - let ftyG = (if fty.IsGenericType then fty.GetGenericTypeDefinition() else fty) - checkTypesSR funTyC ftyG "f" ("SR.GetString(SR.QtmmExpectedFunction)") - let vty = (exprType 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) - 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 = exprType e - checkTypesSR v.Type ety "let" ("SR.GetString(SR.QtmmVarTypeNotMatchRHS)") + let inline mkFEN op l = E(CombTerm(op, l)) + let inline mkFE0 op = E(CombTerm(op, [])) + let inline mkFE1 op x = E(CombTerm(op, [(x:>Expr)])) + let inline mkFE2 op (x, y) = E(CombTerm(op, [(x:>Expr);(y:>Expr)])) + let inline mkFE3 op (x, y, z) = E(CombTerm(op, [(x:>Expr);(y:>Expr);(z:>Expr)]) ) + let inline mkOp v () = v // [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 inline mkVar v = E(VarTerm v ) + let inline mkQuote(a, isTyped) = E(CombTerm(QuoteOp isTyped, [(a:>Expr)] )) + + let inline mkValue (v, ty) = mkFE0 (ValueOp(v, ty, None)) + let inline mkValueWithName (v, ty, nm) = mkFE0 (ValueOp(v, ty, Some nm)) + let inline mkValueWithDefn (v, ty, defn) = mkFE1 (WithValueOp(v, ty)) defn + let inline mkValueG (v:'T) = mkValue(box v, typeof<'T>) + let inline mkLiftedValueOpG (v, ty: System.Type) = //let obj = if ty.IsEnum then System.Enum.ToObject(ty, box v) else box v ValueOp(box v, ty, None) - let mkUnit () = mkValue(null, typeof) - 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 = + let inline mkUnit () = mkValue(null, typeof) + let inline mkAddressOf v = mkFE1 AddressOfOp v + let inline mkSequential (e1, e2) = mkFE2 SequentialOp (e1, e2) + let inline mkTypeTest (e, ty) = mkFE1 (TypeTestOp(ty)) e + let inline mkVarSet (v, e) = mkFE2 VarSetOp (mkVar(v), e) + let inline mkAddressSet (e1, e2) = mkFE2 AddressSetOp (e1, e2) + let inline mkLambda(var, body) = E(LambdaTerm(var, (body:>Expr))) + let inline mkTryWith(e1, v1, e2, v2, e3) = mkFE3 TryWithOp (e1, mkLambda(v1, e2), mkLambda(v2, e3)) + let inline mkTryFinally(e1, e2) = mkFE2 TryFinallyOp (e1, e2) + + let inline mkCoerce (ty, x) = mkFE1 (CoerceOp ty) x + let inline mkNull (ty) = mkFE0 (ValueOp(null, ty, None)) + + let inline mkApplication v = mkFE2 AppOp v + + let inline mkLetRaw v = mkFE2 LetOp v - let mkLetRawWithCheck ((e1, e2) as v) = - checkAppliedLambda (e2, e1) + let inline mkLetRawWithCheck v = mkLetRaw v // Tuples - let mkNewTupleWithType (ty, args:Expr list) = - let mems = FSharpType.GetTupleElements ty |> Array.toList + let inline mkNewTupleWithType (ty, args:Expr list) = + let mems = FSharpType.GetTupleElements ty if (args.Length <> mems.Length) then invalidArg "args" ("SR.GetString(SR.QtupleLengthsDiffer)") - List.iter2(fun mt a -> checkTypesSR mt (exprType a) "args" ("SR.GetString(SR.QtmmTuple)") ) mems args mkFEN (NewTupleOp ty) args - let mkNewTuple (args) = + let inline mkNewTuple (args) = let ty = FSharpType.MakeTupleType(Array.map exprType (Array.ofList args)) mkFEN (NewTupleOp ty) args - let mkTupleGet (ty, n, x) = - checkTypesSR ty (exprType x) "tupleGet" ("SR.GetString(SR.QtmmExprNotMatchTuple)") + let inline mkTupleGet (ty, n, x) = let mems = FSharpType.GetTupleElements ty - if (n < 0 || mems.Length <= n) then invalidArg "n" ("SR.GetString(SR.QtupleAccessOutOfRange)") + if (n < 0 || mems.Length <= n) then invalidArg "n" ("SR.GetString(SR.QtupleAccessOutOfRange)") mkFE1 (TupleGetOp (ty, n)) x // Records - let mkNewRecord (ty, args:list) = + let inline mkNewRecord (ty, args:list) = let mems = FSharpType.GetRecordFields(ty) if (args.Length <> mems.Length) then invalidArg "args" ("SR.GetString(SR.QincompatibleRecordLength)") - List.iter2 (fun (minfo:PropertyInfo) a -> checkTypesSR minfo.PropertyType (exprType a) "recd" ("SR.GetString(SR.QtmmIncorrectArgForRecord)")) (Array.toList mems) args mkFEN (NewRecordOp ty) args // Discriminated unions - let mkNewUnionCase (unionCase:UnionCaseInfo, args:list) = + let inline mkNewUnionCase (unionCase:UnionCaseInfo, args:list) = if Unchecked.defaultof = unionCase then raise (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 (exprType a) "sum" ("SR.GetString(SR.QtmmIncorrectArgForUnion)")) (Array.toList sargs) args mkFEN (NewUnionCaseOp unionCase) args - let mkUnionCaseTest (unionCase:UnionCaseInfo, expr) = + let inline mkUnionCaseTest (unionCase:UnionCaseInfo, expr) = if Unchecked.defaultof = unionCase then raise (ArgumentNullException()) - checkTypesSR unionCase.DeclaringType (exprType expr) "UnionCaseTagTest" ("SR.GetString(SR.QtmmExprTypeMismatch)") mkFE1 (UnionCaseTestOp unionCase) expr // Conditional etc.. - let mkIfThenElse (e, t, f) = - checkTypesSR (exprType t) (exprType f) "cond" ("SR.GetString(SR.QtmmTrueAndFalseMustMatch)") - checkTypesSR (typeof) (exprType e) "cond" ("SR.GetString(SR.QtmmCondMustBeBool)") + let inline mkIfThenElse (e, t, f) = mkFE3 IfThenElseOp (e, t, f) - let mkNewArray (ty, args) = - List.iter (fun a -> checkTypesSR ty (exprType a) "newArray" ("SR.GetString(SR.QtmmInitArray)")) args + let inline mkNewArray (ty, args) = mkFEN (NewArrayOp ty) args - let mkInstanceFieldGet(obj, finfo:FieldInfo) = + let inline mkInstanceFieldGet(obj, finfo:FieldInfo) = if Unchecked.defaultof = finfo then raise (ArgumentNullException()) match finfo.IsStatic with | false -> - checkObj finfo obj mkFE1 (InstanceFieldGetOp finfo) obj | true -> invalidArg "finfo" ("SR.GetString(SR.QstaticWithReceiverObject)") - let mkStaticFieldGet (finfo:FieldInfo) = + let inline mkStaticFieldGet (finfo:FieldInfo) = if Unchecked.defaultof = finfo then raise (ArgumentNullException()) match finfo.IsStatic with | true -> mkFE0 (StaticFieldGetOp finfo) | false -> invalidArg "finfo" ("SR.GetString(SR.QnonStaticNoReceiverObject)") - let mkStaticFieldSet (finfo:FieldInfo, value:Expr) = + let inline mkStaticFieldSet (finfo:FieldInfo, value:Expr) = if Unchecked.defaultof = finfo then raise (ArgumentNullException()) //checkTypesSR (exprType 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) = + let inline mkInstanceFieldSet (obj, finfo:FieldInfo, value:Expr) = if Unchecked.defaultof = finfo then raise (ArgumentNullException()) //checkTypesSR (exprType 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) = + let inline mkCtorCall (ci:ConstructorInfo, args:list) = if Unchecked.defaultof = ci then raise (ArgumentNullException()) - checkArgs (ci.GetParameters()) args mkFEN (NewObjectOp ci) args - let mkDefaultValue (ty:Type) = + let inline mkDefaultValue (ty:Type) = mkFE0 (DefaultValueOp ty) - let mkStaticPropGet (pinfo:PropertyInfo, args:list) = + let inline mkStaticPropGet (pinfo:PropertyInfo, args:list) = if Unchecked.defaultof = pinfo then raise (ArgumentNullException()) mkFEN (StaticPropGetOp pinfo) args // if (not pinfo.CanRead) then invalidArg "pinfo" ("SR.GetString(SR.QreadingSetOnly)") @@ -830,7 +752,7 @@ module Patterns = // | true -> mkFEN (StaticPropGetOp pinfo) args // | false -> invalidArg "pinfo" ("SR.GetString(SR.QnonStaticNoReceiverObject)") - let mkInstancePropGet (obj, pinfo:PropertyInfo, args:list) = + let inline mkInstancePropGet (obj, pinfo:PropertyInfo, args:list) = if Unchecked.defaultof = pinfo then raise (ArgumentNullException()) mkFEN (InstancePropGetOp pinfo) (obj::args) // if (not pinfo.CanRead) then invalidArg "pinfo" ("SR.GetString(SR.QreadingSetOnly)") @@ -841,7 +763,7 @@ module Patterns = // mkFEN (InstancePropGetOp pinfo) (obj::args) // | true -> invalidArg "pinfo" ("SR.GetString(SR.QstaticWithReceiverObject)") - let mkStaticPropSet (pinfo:PropertyInfo, args:list, value:Expr) = + let inline mkStaticPropSet (pinfo:PropertyInfo, args:list, value:Expr) = if Unchecked.defaultof = pinfo then raise (ArgumentNullException()) mkFEN (StaticPropSetOp pinfo) (args@[value]) // if (not pinfo.CanWrite) then invalidArg "pinfo" ("SR.GetString(SR.QwritingGetOnly)") @@ -850,7 +772,7 @@ module Patterns = // | true -> mkFEN (StaticPropSetOp pinfo) (args@[value]) // | false -> invalidArg "pinfo" ("SR.GetString(SR.QnonStaticNoReceiverObject)") - let mkInstancePropSet (obj, pinfo:PropertyInfo, args:list, value:Expr) = + let inline mkInstancePropSet (obj, pinfo:PropertyInfo, args:list, value:Expr) = if Unchecked.defaultof = pinfo then raise (ArgumentNullException()) mkFEN (InstancePropSetOp pinfo) (obj::(args@[value])) // if (not pinfo.CanWrite) then invalidArg "pinfo" ("SR.GetString(SR.QwritingGetOnly)") @@ -861,753 +783,52 @@ module Patterns = // mkFEN (InstancePropSetOp pinfo) (obj::(args@[value])) // | true -> invalidArg "pinfo" ("SR.GetString(SR.QstaticWithReceiverObject)") - let mkInstanceMethodCall (obj, minfo:MethodInfo, args:list) = + let inline mkInstanceMethodCall (obj, minfo:MethodInfo, args:list) = if Unchecked.defaultof = minfo then raise (ArgumentNullException()) - checkArgs (minfo.GetParameters()) args match minfo.IsStatic with - | false -> - checkObj minfo obj - mkFEN (InstanceMethodCallOp minfo) (obj::args) + | false -> mkFEN (InstanceMethodCallOp minfo) (obj::args) | true -> invalidArg "minfo" ("SR.GetString(SR.QstaticWithReceiverObject)") - let mkStaticMethodCall (minfo:MethodInfo, args:list) = + let inline mkStaticMethodCall (minfo:MethodInfo, args:list) = if Unchecked.defaultof = minfo then raise (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) (exprType lowerBound) "lowerBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") - checkTypesSR (typeof) (exprType upperBound) "upperBound" ("SR.GetString(SR.QtmmLowerUpperBoundMustBeInt)") - checkTypesSR (typeof) (v.Type) "for" ("SR.GetString(SR.QtmmLoopBodyMustBeLambdaTakingInteger)") + let inline mkForLoop (v:Var, lowerBound, upperBound, body) = mkFE3 ForIntegerRangeLoopOp (lowerBound, upperBound, mkLambda(v, body)) - let mkWhileLoop (guard, body) = - checkTypesSR (typeof) (exprType guard) "guard" ("SR.GetString(SR.QtmmGuardMustBeBool)") - checkTypesSR (typeof) (exprType body) "body" ("SR.GetString(SR.QtmmBodyMustBeUnit)") + let inline mkWhileLoop (guard, body) = mkFE2 (WhileLoopOp) (guard, body) - let mkNewDelegate (ty, e) = - let nargs = getDelegateNargs ty - let targs = ty.GetGenericArguments() - let ps = targs |> Array.take nargs - let ret = targs.[targs.Length - 1] - let dlfun = Array.foldBack mkFunTy ps ret - checkTypesSR dlfun (exprType e) "ty" ("SR.GetString(SR.QtmmFunTypeNotMatchDelegate)") + let inline mkNewDelegate (ty, e) = mkFE1 (NewDelegateOp ty) e - let mkLet (v, e, b) = - checkBind (v, e) + let inline mkLet (v, e, b) = mkLetRaw (e, mkLambda(v, b)) - //let mkLambdas(vs, b) = mkRLinear mkLambdaRaw (vs, (b:>Expr)) - let mkTupledApplication (f, args) = + //let inline mkLambdas(vs, b) = mkRLinear mkLambdaRaw (vs, (b:>Expr)) + let inline mkTupledApplication (f, args) = match args with | [] -> mkApplication (f, mkUnit()) | [x] -> mkApplication (f, x) | _ -> mkApplication (f, mkNewTuple args) - let mkApplications(f: Expr, es:list>) = mkLLinear mkTupledApplication (f, es) + let inline mkApplications(f: Expr, es:list>) = mkLLinear mkTupledApplication (f, es) - let mkIteratedLambdas(vs, b) = mkRLinear mkLambda (vs, b) + let inline 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 inline mkLetRecRaw v = mkFE1 LetRecOp v + let inline mkLetRecCombRaw v = mkFEN LetRecCombOp v + let inline mkLetRec (ves:(Var*Expr) list, body) = 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. - [] - type ModuleDefinitionBindingResult<'T, 'R> = - | Unique of 'T - | Ambiguous of 'R - - let typeEquals (s:Type) (t:Type) = s = 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() |> 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 = - match parentT.GetMethod(nm, argTs) with - | null -> None - | res -> Some(res) - 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) 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() |> 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 - match ty.GetMethod(nm, argTypes) with - | null -> None - | res -> Some(res) - with _ -> 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() - |> 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) - // 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 = typeof // TODO: find parameter //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 (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() - |> 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() - |> 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) |> 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) |> checkNonNullResult ("fldName", "String.Format(SR.GetString(SR.QfailedToBindField), fldName)") // fxcop may not see "fldName" as an arg - - let bindGenericCctor (tc:Type) = - tc.GetConstructor([| |]) - |> checkNonNullResult ("tc", "SR.GetString(SR.QfailedToBindConstructor)") - - let bindGenericCtor (tc:Type, argTypes:Instantiable) = - 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(Array.ofList argtyps) |> checkNonNullResult ("tc", "SR.GetString(SR.QfailedToBindConstructor)") -#endif - - let bindCtor (tc, argTypes:Instantiable, 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(Array.ofList argtyps) |> 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>, retType, methName, numMethTyargs) = - bindMethodHelper(tc, methName, numMethTyargs, argTypes, retType) - - let bindMeth ((tc:Type, argTypes : list>, 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 = - - [] - type InputState = - { is: ByteStream - istrings: string[] - referencedTypeDefs: Type[] } - - let u_byte_as_int st = st.is.ReadByte() - - let u_bool st = - let b = u_byte_as_int st - (b = 1) - - let u_void (_: 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 len = u_int32 st - st.is.ReadBytes len - - 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 referencedTypeDefs u phase2bytes = - let phase2data = - let st2 = - { is = new ByteStream(phase2bytes, 0, phase2bytes.Length) - istrings = [| |] - 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 - 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.MakeArrayType(1) returns "Int[*]" but we need "Int[]" - | _ -> invalidArg "tys" ("SR.GetString(SR.QexpectedOneType)") - - // let mkNamedTycon (tcName, assembly:Assembly) = - // match assembly.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 (assembly.GetTypes() |> Array.tryFind (fun a -> a.FullName = tcName)) with - // | Some ty -> ty - // | None -> invalidArg "tcName" ("String.Format(SR.GetString(SR.QfailedToBindTypeInAssembly), tcName, assembly.FullName)") // "Available types are:\n%A" tcName assembly (assembly.GetTypes() |> Array.map (fun a -> a.FullName)) - // | ty -> ty - - let decodeNamedTy tc tsR = mkNamedType (tc, tsR) - - let u_assemblyRef 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())") -// | assembly -> assembly - - let u_NamedType st = - let a, b = u_tup2 u_string u_assemblyRef st - - match System.Int32.TryParse(a) with - | (true, idx) when b = "" -> - // 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 - st.referencedTypeDefs.[idx] - | _ -> - // 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, @"(? u_void 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" - - [] - type BindingEnv = - { /// Mapping from variable index to Var object for the variable - vars : Map - /// 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 -> - let a = u_constSpec st - let b = u_dtypes st - let args = u_list u_Expr st - (fun (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 exprType args - f argTys - let tyargs = b env.typeInst - E (CombTerm (a tyargs, args))) - | 1 -> - let x = u_VarRef st - (x >> VarTerm >> E) - | 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 -> 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)) - | 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 referencedTypes u_Expr bytes - - let unpickleReflectedDefns referencedTypes bytes = - unpickleObj 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 exprType 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 @@ -1616,13 +837,6 @@ module Patterns = | 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 [] exception Clash of Var @@ -1656,260 +870,22 @@ module Patterns = let substituteRaw tmsubst e = substituteInExpr Set.empty tmsubst e - // let readToEnd (s : Stream) = - // let n = int s.Length - // let res = Array.zeroCreate n - // let mutable i = 0 - // while (i < n) do - // i <- i + s.Read(res, i, (n - i)) - // res - - let decodedTopResources = new Dictionary(10, HashIdentity.Structural) - -#if !FX_NO_REFLECTION_METADATA_TOKENS -#if FX_NO_REFLECTION_MODULE_HANDLES // not available on Silverlight - [] - 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 - [] - 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() 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 - [] - type ReflectedDefinitionTableKey = - | Key of string - static member GetKey(methodBase:MethodBase) = - // TODO: better key - Key (methodBase.DeclaringType.FullName + "." + methodBase.Name) - //Key(methodBase.Module.ModuleHandle, methodBase.MetadataToken) -#endif - [] - type ReflectedDefinitionTableEntry = Entry of Bindable - - let reflectedDefinitionTable = new Dictionary(10, HashIdentity.Structural) - - let registerReflectedDefinitions (resourceName, bytes, referencedTypes) = - let defns = unpickleReflectedDefns referencedTypes bytes - defns |> List.iter (fun (minfo, exprBuilder) -> - let key = ReflectedDefinitionTableKey.GetKey minfo - lock reflectedDefinitionTable (fun () -> - reflectedDefinitionTable.Add(key, Entry(exprBuilder)))) - decodedTopResources.Add((resourceName), 0) - - let tryGetReflectedDefinition (methodBase: MethodBase, tyargs: Type []) = + let tryGetReflectedDefinitionInstantiated (methodBase:MethodBase) : Expr option = checkNonNull "methodBase" methodBase - let data = - 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) |> Seq.toArray -// #else -// assem.GetCustomAttributes(typeof, 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 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 - + None let cast (expr: Expr) : Expr<'T> = - checkTypesSR (typeof<'T>) (exprType expr) "expr" ("SR.GetString(SR.QtmmExprHasWrongType)") new Expr<'T>(expr.Tree, expr.CustomAttributes) open Patterns type Expr with + member x.WithRange (file : string, sl : int, sc : int, el : int, ec : int) = + let att = Expr.Value((file, sl, sc, el, ec) :> obj, typeof) + EA(x.Tree, [att]) + member x.Substitute substitution = substituteRaw substitution x member x.GetFreeVars () = (freeInExpr x :> seq<_>) member x.Type = exprType x @@ -2065,24 +1041,25 @@ type Expr with static member Cast(source:Expr) = cast source - static member Deserialize(qualifyingType:Type, spliceTypes, spliceExprs, bytes: byte[]) = + static member Deserialize(qualifyingType:Type, spliceTypes, spliceExprs, bytes: byte[]) : Expr = checkNonNull "qualifyingType" qualifyingType checkNonNull "bytes" bytes - deserialize (qualifyingType, [| |], Array.ofList spliceTypes, Array.ofList spliceExprs, bytes) + failwith "not implemented" - static member Deserialize40(qualifyingType:Type, referencedTypes, spliceTypes, spliceExprs, bytes: byte[]) = + static member Deserialize40(qualifyingType:Type, referencedTypes, spliceTypes, spliceExprs, bytes: byte[]) : Expr = checkNonNull "spliceExprs" spliceExprs checkNonNull "spliceTypes" spliceTypes checkNonNull "referencedTypeDefs" referencedTypes checkNonNull "qualifyingType" qualifyingType checkNonNull "bytes" bytes - deserialize (qualifyingType, referencedTypes, spliceTypes, spliceExprs, bytes) + failwith "not implemented" static member RegisterReflectedDefinitions(resource, serializedValue) = Expr.RegisterReflectedDefinitions(resource, serializedValue, [| |]) - static member RegisterReflectedDefinitions(resource, serializedValue, referencedTypes) = - registerReflectedDefinitions( resource, serializedValue, referencedTypes) + static member RegisterReflectedDefinitions(resource, serializedValue, referencedTypes) : unit = + failwith "not implemented" + //registerReflectedDefinitions( resource, serializedValue, referencedTypes) static member GlobalVar<'T>(name) : Expr<'T> = checkNonNull "name" name @@ -2188,19 +1165,6 @@ module DerivedPatterns = | _ -> 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 - - // [] - // 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 [] let (|MethodWithReflectedDefinition|_|) (methodBase) = @@ -2220,50 +1184,51 @@ module ExprShape = let RebuildShapeCombination(shape:obj, arguments) = // preserve the attributes let op, attrs = unbox(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) + EA(CombTerm(op, attrs), arguments) + // 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) [] let rec (|ShapeVar|ShapeLambda|ShapeCombination|) input = From bcbfc197f09c32791fcd0c99b4af9590dfbf50d0 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Wed, 24 Apr 2019 11:19:20 +0200 Subject: [PATCH 28/38] Reflection cleanup (fixed implementations for Invoke/GetValue/SetValue) --- build.fsx | 2 +- src/Fable.Transforms/AST/AST.Fable.fs | 8 +- src/Fable.Transforms/FSharp2Fable.fs | 135 +++++++++++++++++++++----- src/Fable.Transforms/Fable2Babel.fs | 30 +++--- src/fable-library/Reflection.ts | 28 +++--- tests/Main/ExprTests.fs | 31 +++++- 6 files changed, 174 insertions(+), 60 deletions(-) diff --git a/build.fsx b/build.fsx index 311bfedab4..e6dd36accb 100644 --- a/build.fsx +++ b/build.fsx @@ -57,7 +57,7 @@ let buildSplitter projectDir = buildSplitterWithArgs projectDir "" let buildWebpack projectDir = - run ("npx --node-arg \"--max_old_space_size=4096\" webpack --config " + (projectDir "webpack.config.js")) + run ("npx webpack --config " + (projectDir "webpack.config.js")) let buildLibrary() = cleanDirs ["build/fable-library"] diff --git a/src/Fable.Transforms/AST/AST.Fable.fs b/src/Fable.Transforms/AST/AST.Fable.fs index 1fd6fe00b5..3eaf01819f 100644 --- a/src/Fable.Transforms/AST/AST.Fable.fs +++ b/src/Fable.Transforms/AST/AST.Fable.fs @@ -64,10 +64,10 @@ type ParameterInfo = } type MemberInfoKind = - | Property of name : string * typ : Type * fsharp : bool * isStatic : bool * get : Option * set : Option - | Field of name : string * typ : Type * isStatic : bool * get : Option - | Method of genericParameters : string[] * name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * mangledName : string - | Constructor of parameters : ParameterInfo[] * mangledName : string + | Property of name : string * typ : Type * fsharp : bool * isStatic : bool * get : Option * set : Option + | Field of name : string * typ : Type * isStatic : bool * get : Option + | Method of genericParameters : string[] * name : string * parameters : ParameterInfo[] * returnType : Type * isStatic : bool * invoke : Option + | Constructor of parameters : ParameterInfo[] * invoke : Expr | UnionCaseConstructor of tag : int * name : string * parameters : array * mangledName : string * mangledTypeName : string type MemberInfo = diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index fd71f33367..0ce06ed712 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -321,6 +321,13 @@ let private makeLambda2 (a0 : Fable.Type) (a1 : Fable.Type) (body : Fable.Expr - let body = body (Fable.IdentExpr v0) (Fable.IdentExpr v1) Fable.Function(Fable.FunctionKind.Delegate [v0; v1], body, None) +let private makeLambda3 (a0 : Fable.Type) (a1 : Fable.Type) (a2 : Fable.Type) (body : Fable.Expr -> Fable.Expr -> Fable.Expr -> Fable.Expr) = + let v0 = { Fable.Ident.Name = "arg0"; Fable.Ident.Type = a0; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let v1 = { Fable.Ident.Name = "arg1"; Fable.Ident.Type = a1; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let v2 = { Fable.Ident.Name = "arg2"; Fable.Ident.Type = a2; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } + let body = body (Fable.IdentExpr v0) (Fable.IdentExpr v1) (Fable.IdentExpr v2) + Fable.Function(Fable.FunctionKind.Delegate [v0; v1; v2], body, None) + let private makeLambda0 (body : Fable.Expr -> Fable.Expr) = let target = { Fable.Ident.Name = "target"; Fable.Ident.Type = Fable.Type.Any; Fable.Ident.Kind = Fable.IdentKind.UnspecifiedIdent; Fable.Ident.IsMutable = false; Fable.Ident.IsCompilerGenerated = false; Fable.Ident.Range = None } let body = body (Fable.IdentExpr target) @@ -328,6 +335,11 @@ let private makeLambda0 (body : Fable.Expr -> Fable.Expr) = +let rec private (|Snoc|Nil|) (l : list<'a>) = + match l with + | [] -> Nil + | v :: Nil -> Snoc ([], v) + | a :: Snoc(h, t) -> Snoc(a :: h, t) let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFunctionOrValue) = @@ -338,7 +350,7 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun elif m.IsValue then let get = - makeLambda Fable.Type.Any (fun t -> + makeLambda2 Fable.Type.Any (Fable.Array Fable.Any) (fun t _ -> if m.IsInstanceMember then let ft = makeType com ctx.GenericArgs m.FullType Fable.Get(t, Fable.FieldGet(m.CompiledName, m.IsMutable, ft), ft, None) @@ -348,7 +360,7 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun let set = if m.IsMutable then - makeLambda2 Fable.Type.Any Fable.Type.Any (fun t v -> + makeLambda3 Fable.Type.Any (Fable.Array Fable.Any) Fable.Type.Any (fun t _ v -> if m.IsInstanceMember then let ft = makeType com ctx.GenericArgs m.FullType Fable.Set(t, Fable.FieldSet(m.CompiledName, ft), v, None) @@ -368,22 +380,46 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun let ft = makeType com ctx.GenericArgs m.ReturnParameter.Type let get = - if m.HasGetterMethod && m.GetterMethod.GenericParameters.Count = 0 && (m.GetterMethod.CurriedParameterGroups |> Seq.sumBy (fun g -> g.Count)) = 0 then + if m.HasGetterMethod && m.GetterMethod.GenericParameters.Count = 0 then let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value - makeLambda decl (fun t -> - let callee = if m.IsInstanceMember then Some t else None - makeCallFrom com ctx None ft false [] callee [] m.GetterMethod - ) |> Some + match m.GetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with + | [] -> + makeLambda decl (fun t -> + let callee = if m.IsInstanceMember then Some t else None + makeCallFrom com ctx None ft false [] callee [] m.GetterMethod + ) |> Some + | args -> + makeLambda2 decl (Fable.Type.Array Fable.Any) (fun t idx -> + let callee = if m.IsInstanceMember then Some t else None + let args = + args |> List.mapi (fun i a -> + let t = makeType com ctx.GenericArgs a.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(idx, Fable.ExprGet index, t, None) + ) + makeCallFrom com ctx None ft false [] callee args m.GetterMethod + ) |> Some else None let set = - if m.HasSetterMethod && m.SetterMethod.GenericParameters.Count = 0 && (m.SetterMethod.CurriedParameterGroups |> Seq.sumBy (fun g -> g.Count)) = 1 then + if m.HasSetterMethod && m.SetterMethod.GenericParameters.Count = 0 then let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value - makeLambda2 decl ft (fun t value -> - let callee = if m.IsInstanceMember then Some t else None - makeCallFrom com ctx None ft false [] callee [value] m.SetterMethod - ) |> Some + match m.SetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with + | Nil -> + None + | Snoc(idx, _val) -> + makeLambda3 decl (Fable.Array Fable.Any) Fable.Any (fun t ia value -> + let callee = if m.IsInstanceMember then Some t else None + let idx = + idx |> List.mapi (fun i a -> + let t = makeType com ctx.GenericArgs a.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ia, Fable.ExprGet index, t, None) + ) + makeCallFrom com ctx None ft false [] callee (idx @ [value]) m.SetterMethod + ) |> Some + else None Some { @@ -396,14 +432,36 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } ) - let mangledName = Helpers.getMemberDeclarationName com m + let invoke = + makeLambda (Fable.Array Fable.Any) (fun ip -> + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toList + let args = pars |> List.mapi (fun i p -> + let t = makeType com ctx.GenericArgs p.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ip, Fable.ExprGet index, t, None) + ) + + let info = + { Fable.ThisArg = None + Fable.Args = args + Fable.SignatureArgTypes = Fable.SignatureKind.NoUncurrying + Fable.Spread = Fable.SpreadKind.NoSpread + Fable.IsBaseOrSelfConstructorCall = false + } + + staticCall None decl info (makeValueFrom com ctx None m) + ) + + + //let mangledName = Helpers.getMemberDeclarationName com m Some { - Fable.MemberInfo.Kind = Fable.Constructor(pars, mangledName) + Fable.MemberInfo.Kind = Fable.Constructor(pars, invoke) Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } - else // m.IsPropertyGetterMethod || m.IsPropertySetterMethod || m.IsValCompiledAsMethod || m.IsInstanceMember then + else let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.choose (fun p -> let t = makeType com ctx.GenericArgs p.Type @@ -412,11 +470,31 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun else None ) + + + let invoke = + if Helpers.isInline m then + None + else + makeLambda2 (Fable.Any) (Fable.Array Fable.Any) (fun target ip -> + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toList + let args = pars |> List.mapi (fun i p -> + let t = makeType com ctx.GenericArgs p.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ip, Fable.ExprGet index, t, None) + ) + if m.IsInstanceMember then + makeCallFrom com ctx None decl false [] (Some target) args m + else + makeCallFrom com ctx None decl false [] (None) args m + ) |> Some + + let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type - let mangledName = Helpers.getMemberDeclarationName com m let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) Some { - Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, mangledName) + Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, invoke) Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) } @@ -1029,11 +1107,21 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar let special = if ent.IsFSharpRecord then - let fields = ent.FSharpFields |> Seq.toArray - let parameters = fields |> Array.map (fun f -> { Fable.ParameterInfo.Name = f.Name; Fable.Type = makeType com ctx.GenericArgs f.FieldType}) - let ctor = "new " + getEntityDeclarationName com ent + let fields = ent.FSharpFields |> Seq.toList + let parameters = fields |> List.map (fun f -> { Fable.ParameterInfo.Name = f.Name; Fable.Type = makeType com ctx.GenericArgs f.FieldType}) + // let ctor = "new " + getEntityDeclarationName com ent + let invoke = + makeLambda (Fable.Array Fable.Any) (fun ip -> + let args = + fields |> List.mapi (fun i a -> + let t = makeType com ctx.GenericArgs a.FieldType + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ip, Fable.ExprGet index, t, None) + ) + Fable.Value(Fable.NewRecord(args, Fable.DeclaredRecord ent, []), None) + ) Seq.concat [ - ent.FSharpFields |> Seq.map (fun m -> + fields |> List.map (fun m -> let ft = makeType com ctx.GenericArgs m.FieldType { Fable.MemberInfo.Kind = Fable.Property(m.Name, ft, true, false, None, None) @@ -1041,8 +1129,9 @@ let private transformMemberReflectionInfos (com: FableCompiler) ctx (ent : FShar } ) - Seq.singleton { - Fable.MemberInfo.Kind = Fable.Constructor(parameters, ctor) + + List.singleton { + Fable.MemberInfo.Kind = Fable.Constructor(List.toArray parameters, invoke) Fable.Attributes = [||] } diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index e0d2566596..c13eee6438 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -385,12 +385,12 @@ module Util = let par = coreValue com ctx "Reflection" "NParameterInfo" NewExpression(par, [| StringLiteral p.Name; transformTypeInfo com ctx r [||] genMap p.Type |]) :> Expression - let newConstructor (self : Expression) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = + let newConstructor (self : Expression) (attributes : ArrayExpression) (parameters : array) (invoke : Expression) = let ctor = coreValue com ctx "Reflection" "NConstructorInfo" let parameters = parameters |> Array.map newParameter NewExpression(ctor, [|self; ArrayExpression parameters; invoke; attributes |]) :> Expression - let newMethod (self : Expression) (genericParameters : string[]) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (parameters : array) (invoke : ArrowFunctionExpression) = + let newMethod (self : Expression) (genericParameters : string[]) (isStatic : bool) (ret : Fable.Type) (name : string) (attributes : ArrayExpression) (parameters : array) (invoke : Expression) = let meth = coreValue com ctx "Reflection" "NMethodInfo" let parameters = parameters |> Array.map newParameter let ret = transformTypeInfo com ctx r [||] genMap ret @@ -448,24 +448,18 @@ module Util = ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) newUnionCase self tag name attributes pars invoke - | Fable.MemberInfoKind.Constructor(pars, mangledName) -> - let invoke = - let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) - let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression - ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - + | Fable.MemberInfoKind.Constructor(pars, invoke) -> + let invoke = com.TransformAsExpr(ctx, invoke) newConstructor self attributes pars invoke - | Fable.MemberInfoKind.Method(genericParameters, name, pars, ret, isStatic, mangledName) -> - let invoke = - let args = pars |> Array.mapi (fun i p -> Identifier(sprintf "a%d" i)) - let args = - if isStatic then args - else Array.append [| Identifier "__self" |] args - - let body = CallExpression(Identifier(mangledName), Array.map (fun a -> a :> Expression) args) :> Expression - ArrowFunctionExpression(Array.map toPattern args, U2.Case2 body) - //FunctionExpression(Array.map toPattern args, BlockStatement [| ReturnStatement body :> Statement |]) + | Fable.MemberInfoKind.Method(genericParameters, name, pars, ret, isStatic, invoke) -> + let invoke = + match invoke with + | Some invoke -> com.TransformAsExpr(ctx, invoke) + | _ -> + ArrowFunctionExpression([||], U2.Case1 (BlockStatement [| + ThrowStatement(NewExpression(Identifier "Error", [| StringLiteral "cannot invoke method" :> Expression |])) :> Statement + |])) :> Expression newMethod self genericParameters isStatic ret name attributes pars invoke diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index d9173706aa..9576f1f511 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -108,7 +108,7 @@ export class NMethodInfo extends NMethodBase { parameters: NParameterInfo[], returnType: NTypeInfo, isStatic: boolean, - private invoke: (...args: any[]) => any, + private invoke: (target: any, args: any[]) => any, attributes: CustomAttribute[], private declaration?: NMethodInfo, ) { @@ -205,9 +205,9 @@ export class NMethodInfo extends NMethodBase { args = args || []; if (!this.IsStatic) { if (!target) { throw new Error(`MethodInfo ${this.toPrettyString()} cannot be called without a this argument`); } - return this.invoke.apply(null, [target, ...args]); + return this.invoke(target, args); } else { - return this.invoke.apply(null, args); + return this.invoke(null, args); } } } @@ -216,7 +216,7 @@ export class NConstructorInfo extends NMethodBase { constructor( declaringType: NTypeInfo, parameters: NParameterInfo[], - private invoke: (...args: any[]) => any, + private invoke: (args: any[]) => any, attributes: CustomAttribute[], ) { super(declaringType, ".ctor", parameters, true, attributes); @@ -240,7 +240,7 @@ export class NConstructorInfo extends NMethodBase { public Invoke(args: any[]) { args = args || []; - return this.invoke.apply(null, args); + return this.invoke(args); } } @@ -300,8 +300,8 @@ export class NPropertyInfo extends NMemberInfo { public IsStatic: boolean, public IsFSharp: boolean, attributes: CustomAttribute[], - private get?: (target: any) => any, - private set?: (target: any, value: any) => void, + private get?: (target: any, index: any[]) => any, + private set?: (target: any, index: any[], value: any) => void, ) { super(DeclaringType, Name, attributes); @@ -329,7 +329,11 @@ export class NPropertyInfo extends NMemberInfo { const getterName = "get_" + this.Name; const mems = this.DeclaringType.GetAllMembers(); const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); - if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } + if (idx >= 0) { + return mems[idx] as NMethodInfo; + } else { + return null; + } } public get_SetMethod() { @@ -341,7 +345,7 @@ export class NPropertyInfo extends NMemberInfo { public GetValue(target: any, index: any[]): any { if (this.get) { - return this.get(target); + return this.get(target, index); } else if (this.IsFSharp) { // TODO: mangled-names???? if (this.Name in target) { @@ -359,7 +363,7 @@ export class NPropertyInfo extends NMemberInfo { public SetValue(target: any, value: any, index: any[]) { if (this.set) { - return this.set(target, value); + return this.set(target, index, value); } else if (this.IsFSharp) { target[this.Name] = value; } else { @@ -1056,7 +1060,7 @@ export function anonRecord(...fields: Array<[string, NTypeInfo]>): NTypeInfo { return res; } - const ctor = new NConstructorInfo(self, pars, (...args) => makeAnonRecord(makeObj(args)), []); + const ctor = new NConstructorInfo(self, pars, (args) => makeAnonRecord(makeObj(args)), []); mems.push(ctor as NMemberInfo); return mems; @@ -1128,7 +1132,7 @@ export function createMethod(decl: NTypeInfo, name: string, mpars: string[], mar return found.get_IsGenericMethod() ? found.MakeGenericMethod(margs) : found; } else { const pp = mpars.map ((n) => getGenericParameter(n)); - const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_) => { throw new Error("cannot invoke " + decl.fullname + "." + name); }), []); + const meth = new NMethodInfo(decl, pp, name, declaredArgs.map((a, i) => new NParameterInfo("arg" + i, a)), ret, isStatic, ((_target, _args) => { throw new Error("cannot invoke " + decl.fullname + "." + name); }), []); decl.mems.push(meth); return meth.get_IsGenericMethod() ? meth.MakeGenericMethod(margs) : meth; } diff --git a/tests/Main/ExprTests.fs b/tests/Main/ExprTests.fs index 8ba600bc04..5a93c81973 100644 --- a/tests/Main/ExprTests.fs +++ b/tests/Main/ExprTests.fs @@ -61,15 +61,17 @@ type System.Type with member x.ToPrettyString() : string = jsNative type Classy(a : int, b : string) = + let mutable a = a + let mutable b = b member x.Yeah with get() = b - and set (_ : string) = () + and set (v : string) = b <- v member x.DoIt(c : int) = a*c member x.Item with get(i : int) = a + i - and set (i : int) (v : int) = () + and set (i : int) (v : int) = a <- v - i type MyUnion = | Values of int * int @@ -232,4 +234,29 @@ let tests = | _ -> () + testCase "Property Get/SetValue working (indexed)" <| fun () -> + let instance = Classy(10, "11") + let prop = typeof.GetProperty "Item" + let test = prop.GetValue(instance, [| 15 :> obj |]) + equal (25 :> obj) test + prop.SetValue(instance, 24, [| 15 :> obj |]) + equal 24 instance.[15] + + + testCase "Property Get/SetValue working" <| fun () -> + let instance = Classy(10, "11") + let prop = typeof.GetProperty "Yeah" + let test = prop.GetValue(instance) + equal ("11" :> obj) test + prop.SetValue(instance, "123" :> obj) + equal "123" instance.Yeah + + // let e = <@@ fun a -> a + 1 @@> + // match e.CustomAttributes with + // | [Value((:? (string * int * int * int * int) as tup), t)] -> + // let (file, sl, sc, el, ec) = tup + // failwithf "%A" tup + // | _ -> + // failwith "no debug info" + ] \ No newline at end of file From be89aa6fc999ebda4751610f667ae932ae5d4880 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Wed, 24 Apr 2019 16:03:39 +0200 Subject: [PATCH 29/38] Quotations: fixed nasty but in RebuildShapeCombination --- src/fable-library/Quotations.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index 417fee940b..c5623c14d3 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -1184,7 +1184,7 @@ module ExprShape = let RebuildShapeCombination(shape:obj, arguments) = // preserve the attributes let op, attrs = unbox(shape) - EA(CombTerm(op, attrs), arguments) + EA(CombTerm(op, arguments), attrs) // let e = // match op, arguments with // | AppOp, [f;x] -> mkApplication(f, x) From 2aa205ae93f0a97e942cd452295c6da9f4384450 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Wed, 24 Apr 2019 18:56:24 +0200 Subject: [PATCH 30/38] added FABLE_QUOTATIONS define to enable quotation features --- src/Fable.Cli/Parser.fs | 1 + src/Fable.Transforms/FSharp2Fable.fs | 427 ++++++++++++------------ src/Fable.Transforms/Global/Compiler.fs | 1 + src/fable-standalone/src/Main.fs | 1 + tests/splitter.config.js | 2 + 5 files changed, 220 insertions(+), 212 deletions(-) diff --git a/src/Fable.Cli/Parser.fs b/src/Fable.Cli/Parser.fs index 4c4d18f73b..319eaa45fd 100644 --- a/src/Fable.Cli/Parser.fs +++ b/src/Fable.Cli/Parser.fs @@ -55,6 +55,7 @@ let toCompilerOptions (msg: Message): CompilerOptions = clampByteArrays = msg.clampByteArrays verbose = GlobalParams.Singleton.Verbose outputPublicInlinedFunctions = Array.contains "FABLE_REPL_LIB" msg.define + quotations = Array.contains "FABLE_QUOTATIONS" msg.define precompiledLib = None } diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 0ce06ed712..bf4792399e 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -343,140 +343,97 @@ let rec private (|Snoc|Nil|) (l : list<'a>) = let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFunctionOrValue) = - - if m.IsEvent || m.IsEventAddMethod || m.IsEventRemoveMethod then - // TODO: support these? - None - elif m.IsValue then - - let get = - makeLambda2 Fable.Type.Any (Fable.Array Fable.Any) (fun t _ -> - if m.IsInstanceMember then - let ft = makeType com ctx.GenericArgs m.FullType - Fable.Get(t, Fable.FieldGet(m.CompiledName, m.IsMutable, ft), ft, None) - else - Util.makeValueFrom com ctx None m - ) |> Some - - let set = - if m.IsMutable then - makeLambda3 Fable.Type.Any (Fable.Array Fable.Any) Fable.Type.Any (fun t _ v -> + if com.Options.quotations then + if m.IsEvent || m.IsEventAddMethod || m.IsEventRemoveMethod then + // TODO: support these? + None + elif m.IsValue then + + let get = + makeLambda2 Fable.Type.Any (Fable.Array Fable.Any) (fun t _ -> if m.IsInstanceMember then let ft = makeType com ctx.GenericArgs m.FullType - Fable.Set(t, Fable.FieldSet(m.CompiledName, ft), v, None) + Fable.Get(t, Fable.FieldGet(m.CompiledName, m.IsMutable, ft), ft, None) else - let ref = Util.memberRef com ctx None m - Fable.Set(ref, Fable.SetKind.VarSet, v, None) - ) |> Some - else - None - - - Some { - Fable.MemberInfo.Kind = Fable.Property(m.CompiledName, makeType com ctx.GenericArgs m.FullType, true, not m.IsInstanceMember, get, set) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - elif m.IsProperty then - let ft = makeType com ctx.GenericArgs m.ReturnParameter.Type - - let get = - if m.HasGetterMethod && m.GetterMethod.GenericParameters.Count = 0 then - let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value - match m.GetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with - | [] -> - makeLambda decl (fun t -> - let callee = if m.IsInstanceMember then Some t else None - makeCallFrom com ctx None ft false [] callee [] m.GetterMethod - ) |> Some - | args -> - makeLambda2 decl (Fable.Type.Array Fable.Any) (fun t idx -> - let callee = if m.IsInstanceMember then Some t else None - let args = - args |> List.mapi (fun i a -> - let t = makeType com ctx.GenericArgs a.Type - let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) - Fable.Get(idx, Fable.ExprGet index, t, None) - ) - makeCallFrom com ctx None ft false [] callee args m.GetterMethod + Util.makeValueFrom com ctx None m + ) |> Some + + let set = + if m.IsMutable then + makeLambda3 Fable.Type.Any (Fable.Array Fable.Any) Fable.Type.Any (fun t _ v -> + if m.IsInstanceMember then + let ft = makeType com ctx.GenericArgs m.FullType + Fable.Set(t, Fable.FieldSet(m.CompiledName, ft), v, None) + else + let ref = Util.memberRef com ctx None m + Fable.Set(ref, Fable.SetKind.VarSet, v, None) ) |> Some - else - None - let set = - if m.HasSetterMethod && m.SetterMethod.GenericParameters.Count = 0 then - let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value - - match m.SetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with - | Nil -> + else None - | Snoc(idx, _val) -> - makeLambda3 decl (Fable.Array Fable.Any) Fable.Any (fun t ia value -> - let callee = if m.IsInstanceMember then Some t else None - let idx = - idx |> List.mapi (fun i a -> - let t = makeType com ctx.GenericArgs a.Type - let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) - Fable.Get(ia, Fable.ExprGet index, t, None) - ) - makeCallFrom com ctx None ft false [] callee (idx @ [value]) m.SetterMethod - ) |> Some - - else - None - Some { - Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, ft, false, not m.IsInstanceMember, get, set) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - elif m.IsConstructor then - let pars = - m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> - { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } - ) - - let invoke = - makeLambda (Fable.Array Fable.Any) (fun ip -> - let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value - let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toList - let args = pars |> List.mapi (fun i p -> - let t = makeType com ctx.GenericArgs p.Type - let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) - Fable.Get(ip, Fable.ExprGet index, t, None) - ) - - let info = - { Fable.ThisArg = None - Fable.Args = args - Fable.SignatureArgTypes = Fable.SignatureKind.NoUncurrying - Fable.Spread = Fable.SpreadKind.NoSpread - Fable.IsBaseOrSelfConstructorCall = false - } - - staticCall None decl info (makeValueFrom com ctx None m) - ) - - - //let mangledName = Helpers.getMemberDeclarationName com m - Some { - Fable.MemberInfo.Kind = Fable.Constructor(pars, invoke) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } - else - let pars = - m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.choose (fun p -> - let t = makeType com ctx.GenericArgs p.Type - if t <> Fable.Type.Unit then - Some { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = t } + Some { + Fable.MemberInfo.Kind = Fable.Property(m.CompiledName, makeType com ctx.GenericArgs m.FullType, true, not m.IsInstanceMember, get, set) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsProperty then + let ft = makeType com ctx.GenericArgs m.ReturnParameter.Type + + let get = + if m.HasGetterMethod && m.GetterMethod.GenericParameters.Count = 0 then + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + match m.GetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with + | [] -> + makeLambda decl (fun t -> + let callee = if m.IsInstanceMember then Some t else None + makeCallFrom com ctx None ft false [] callee [] m.GetterMethod + ) |> Some + | args -> + makeLambda2 decl (Fable.Type.Array Fable.Any) (fun t idx -> + let callee = if m.IsInstanceMember then Some t else None + let args = + args |> List.mapi (fun i a -> + let t = makeType com ctx.GenericArgs a.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(idx, Fable.ExprGet index, t, None) + ) + makeCallFrom com ctx None ft false [] callee args m.GetterMethod + ) |> Some else - None - ) + None + let set = + if m.HasSetterMethod && m.SetterMethod.GenericParameters.Count = 0 then + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + + match m.SetterMethod.CurriedParameterGroups |> Seq.concat |> Seq.toList with + | Nil -> + None + | Snoc(idx, _val) -> + makeLambda3 decl (Fable.Array Fable.Any) Fable.Any (fun t ia value -> + let callee = if m.IsInstanceMember then Some t else None + let idx = + idx |> List.mapi (fun i a -> + let t = makeType com ctx.GenericArgs a.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ia, Fable.ExprGet index, t, None) + ) + makeCallFrom com ctx None ft false [] callee (idx @ [value]) m.SetterMethod + ) |> Some + else + None + Some { + Fable.MemberInfo.Kind = Fable.Property(m.DisplayName, ft, false, not m.IsInstanceMember, get, set) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + elif m.IsConstructor then + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.map (fun p -> + { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = makeType com ctx.GenericArgs p.Type } + ) - let invoke = - if Helpers.isInline m then - None - else - makeLambda2 (Fable.Any) (Fable.Array Fable.Any) (fun target ip -> + let invoke = + makeLambda (Fable.Array Fable.Any) (fun ip -> let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toList let args = pars |> List.mapi (fun i p -> @@ -484,19 +441,64 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) Fable.Get(ip, Fable.ExprGet index, t, None) ) - if m.IsInstanceMember then - makeCallFrom com ctx None decl false [] (Some target) args m + + let info = + { Fable.ThisArg = None + Fable.Args = args + Fable.SignatureArgTypes = Fable.SignatureKind.NoUncurrying + Fable.Spread = Fable.SpreadKind.NoSpread + Fable.IsBaseOrSelfConstructorCall = false + } + + staticCall None decl info (makeValueFrom com ctx None m) + ) + + + //let mangledName = Helpers.getMemberDeclarationName com m + + Some { + Fable.MemberInfo.Kind = Fable.Constructor(pars, invoke) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + + else + let pars = + m.CurriedParameterGroups |> Seq.concat |> Seq.toArray |> Array.choose (fun p -> + let t = makeType com ctx.GenericArgs p.Type + if t <> Fable.Type.Unit then + Some { Fable.ParameterInfo.Name = p.DisplayName; Fable.Type = t } else - makeCallFrom com ctx None decl false [] (None) args m - ) |> Some + None + ) - let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type - let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) - Some { - Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, invoke) - Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) - } + let invoke = + if Helpers.isInline m then + None + else + makeLambda2 (Fable.Any) (Fable.Array Fable.Any) (fun target ip -> + let decl = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List []) m.DeclaringEntity.Value + let pars = m.CurriedParameterGroups |> Seq.concat |> Seq.toList + let args = pars |> List.mapi (fun i p -> + let t = makeType com ctx.GenericArgs p.Type + let index = Fable.Value(Fable.ValueKind.NumberConstant(float i, NumberKind.Int32), None) + Fable.Get(ip, Fable.ExprGet index, t, None) + ) + if m.IsInstanceMember then + makeCallFrom com ctx None decl false [] (Some target) args m + else + makeCallFrom com ctx None decl false [] (None) args m + ) |> Some + + + let ret = makeType com ctx.GenericArgs m.ReturnParameter.Type + let parNames = m.GenericParameters |> Seq.toArray |> Array.map (fun p -> p.Name) + Some { + Fable.MemberInfo.Kind = Fable.Method(parNames, m.CompiledName, pars, ret, not m.IsInstanceMember, invoke) + Fable.Attributes = m.Attributes |> Seq.toArray |> Array.choose (transformAttribute com ctx) + } + else + None @@ -971,88 +973,89 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = | BasicPatterns.Quote expr -> - - - //let! dummy = transformExpr com ctx expr - - let data = QuotationPickler.serialize com ctx expr - //data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) - - let! values = - data.values |> Array.toList |> trampolineListMap (fun v -> - trampoline { - let! value = - trampoline { - if isInline v then - match ctx.ScopeInlineValues |> List.tryFind (fun (vi,_) -> obj.Equals(vi, v)) with - | Some (_,fsExpr) -> - - return! transformExpr com ctx fsExpr - | None -> - return "Cannot resolve locally inlined value: " + v.DisplayName - |> addErrorAndReturnNull com ctx.InlinePath None - else - return makeValueFrom com ctx None v - } - - - return { - Fable.ValueData.name = v.DisplayName - Fable.ValueData.typ = makeType com ctx.GenericArgs v.FullType - Fable.ValueData.expr = value - } - } - ) - - let fableData = - { - Fable.ExprData.typ = makeType com ctx.GenericArgs data.typ - Fable.variables = - data.variables |> Array.map (fun v -> - { - Fable.VarData.name = v.name; - Fable.VarData.typ = makeType com ctx.GenericArgs v.typ - Fable.VarData.isMutable = v.isMutable - } - ) - Fable.values = List.toArray values + if com.Options.quotations then + + //let! dummy = transformExpr com ctx expr + + let data = QuotationPickler.serialize com ctx expr + //data |> sprintf "%A" |> addWarning com ctx.InlinePath (makeRangeFrom fsExpr) + + let! values = + data.values |> Array.toList |> trampolineListMap (fun v -> + trampoline { + let! value = + trampoline { + if isInline v then + match ctx.ScopeInlineValues |> List.tryFind (fun (vi,_) -> obj.Equals(vi, v)) with + | Some (_,fsExpr) -> + + return! transformExpr com ctx fsExpr + | None -> + return "Cannot resolve locally inlined value: " + v.DisplayName + |> addErrorAndReturnNull com ctx.InlinePath None + else + return makeValueFrom com ctx None v + } + + + return { + Fable.ValueData.name = v.DisplayName + Fable.ValueData.typ = makeType com ctx.GenericArgs v.FullType + Fable.ValueData.expr = value + } + } + ) + + let fableData = + { + Fable.ExprData.typ = makeType com ctx.GenericArgs data.typ + Fable.variables = + data.variables |> Array.map (fun v -> + { + Fable.VarData.name = v.name; + Fable.VarData.typ = makeType com ctx.GenericArgs v.typ + Fable.VarData.isMutable = v.isMutable + } + ) + Fable.values = List.toArray values - Fable.ExprData.types = - data.types |> Array.map (fun d -> - match d with - | Choice1Of2 t -> makeType com ctx.GenericArgs t - | Choice2Of2(d,targs) -> makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List targs) d - ) + Fable.ExprData.types = + data.types |> Array.map (fun d -> + match d with + | Choice1Of2 t -> makeType com ctx.GenericArgs t + | Choice2Of2(d,targs) -> makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List targs) d + ) - Fable.ExprData.members = - data.members |> Array.map (fun m -> - match m with - | QuotationPickler.MemberDescription.Member (m, targs, margs) -> - let d = m.DeclaringEntity.Value - let t = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List(targs)) d - let mem = transformMemberInfo com ctx m |> Option.get - let margs = margs |> List.toArray |> Array.map (makeType com ctx.GenericArgs) - d, t, mem, margs - | QuotationPickler.MemberDescription.UnionCase (c, targs) -> - let d = c.ReturnType.TypeDefinition - let t = makeType com ctx.GenericArgs c.ReturnType - let mem = transformUnionCaseAsMember com ctx c - d, t, mem, [||] - ) - - Fable.literals = - data.literals |> Array.map (fun (v,t) -> - let ft = makeType com ctx.GenericArgs t - Replacements.makeTypeConst (makeRangeFrom fsExpr) ft v - ) - - Fable.data = data.data - } + Fable.ExprData.members = + data.members |> Array.map (fun m -> + match m with + | QuotationPickler.MemberDescription.Member (m, targs, margs) -> + let d = m.DeclaringEntity.Value + let t = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List(targs)) d + let mem = transformMemberInfo com ctx m |> Option.get + let margs = margs |> List.toArray |> Array.map (makeType com ctx.GenericArgs) + d, t, mem, margs + | QuotationPickler.MemberDescription.UnionCase (c, targs) -> + let d = c.ReturnType.TypeDefinition + let t = makeType com ctx.GenericArgs c.ReturnType + let mem = transformUnionCaseAsMember com ctx c + d, t, mem, [||] + ) + + Fable.literals = + data.literals |> Array.map (fun (v,t) -> + let ft = makeType com ctx.GenericArgs t + Replacements.makeTypeConst (makeRangeFrom fsExpr) ft v + ) + + Fable.data = data.data + } - return Fable.Quote(false, fableData, makeRangeFrom fsExpr) - // return "Quotes are not currently supported by Fable" - // |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) + return Fable.Quote(false, fableData, makeRangeFrom fsExpr) + else + return "Quotes can be enabled by adding a define FABLE_QUOTATIONS to your fable-config " + |> addErrorAndReturnNull com ctx.InlinePath (makeRangeFrom fsExpr) // TODO: Ask. I see this when accessing Result types (all structs?) | BasicPatterns.AddressOf(expr) -> diff --git a/src/Fable.Transforms/Global/Compiler.fs b/src/Fable.Transforms/Global/Compiler.fs index 2a737b9b3c..1896a8b066 100644 --- a/src/Fable.Transforms/Global/Compiler.fs +++ b/src/Fable.Transforms/Global/Compiler.fs @@ -7,6 +7,7 @@ type CompilerOptions = /// Meant for precompiled libraries (like the Repl Lib) /// to make public inlined functions part of the JS outputPublicInlinedFunctions: bool + quotations: bool /// Mainly intended for the REPL to compile REPL lib calls precompiledLib: (string -> (string*string) option) option } diff --git a/src/fable-standalone/src/Main.fs b/src/fable-standalone/src/Main.fs index 74bbafb23a..1f8a8bb485 100644 --- a/src/fable-standalone/src/Main.fs +++ b/src/fable-standalone/src/Main.fs @@ -224,6 +224,7 @@ let makeCompiler fableLibrary fileName (project: Project) precompiledLib = clampByteArrays = false verbose = false outputPublicInlinedFunctions = false + quotations = true precompiledLib = precompiledLib } let com = Compiler(fileName, project, options, fableLibrary) com diff --git a/tests/splitter.config.js b/tests/splitter.config.js index 53fba345c4..7bcd00bf8c 100644 --- a/tests/splitter.config.js +++ b/tests/splitter.config.js @@ -24,6 +24,8 @@ function defineConstants() { if (process.argv.find(v => v === "-d:OPTIMIZE_FCS")) { ar.push("OPTIMIZE_FCS"); } + ar.push("FABLE_QUOTATIONS"); + return ar; } From 0edf884bca0750f01dee07e29a4a0a5561c6aa32 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Sat, 27 Apr 2019 09:16:22 +0200 Subject: [PATCH 31/38] - now importing precompiled reflection-infos - Reflection-Infos no longer checked using instanceof (causing problems in repl) --- src/Fable.Transforms/Fable2Babel.fs | 20 +++- src/Fable.Transforms/Global/Prelude.fs | 2 + src/Fable.Transforms/Replacements.fs | 10 ++ src/fable-library/Reflection.ts | 142 ++++++++++++------------- 4 files changed, 99 insertions(+), 75 deletions(-) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index c13eee6438..c7a644df94 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -633,9 +633,25 @@ module Util = /// Check if the entity is actually declared in JS code if ent.IsInterface || FSharp2Fable.Util.isErasedEntity ent - // TODO!!! Get reflection info from types in precompiled libs || FSharp2Fable.Util.isReplacementCandidate ent then - genericEntity ent generics + + match t with + | Fable.DeclaredType(ent, args) -> + match ent.TryFullName with + | Some fullname -> + let args = args |> List.map (fun a -> Fable.Value(Fable.TypeInfo(a), None)) + let call = + com.Options.precompiledLib + |> Option.bind (fun tryLib -> tryLib fullname) + |> Option.map (Replacements.precompiledLibReflection r args) + + match call with + | Some call -> com.TransformAsExpr(ctx, call) + | None -> genericEntity ent generics + | _ -> + genericEntity ent generics + | _ -> + genericEntity ent generics else let reflectionMethodExpr = FSharp2Fable.Util.entityRefWithSuffix com ent Naming.reflectionSuffix CallExpression(com.TransformAsExpr(ctx, reflectionMethodExpr), generics) :> Expression diff --git a/src/Fable.Transforms/Global/Prelude.fs b/src/Fable.Transforms/Global/Prelude.fs index 12b1fab593..2f6b674150 100644 --- a/src/Fable.Transforms/Global/Prelude.fs +++ b/src/Fable.Transforms/Global/Prelude.fs @@ -239,6 +239,7 @@ module Naming = check 0 type MemberPart = + | ReflectionMemberPart | InstanceMemberPart of string * overloadSuffix: string | StaticMemberPart of string * overloadSuffix: string | NoMemberPart @@ -260,6 +261,7 @@ module Naming = let private buildName sanitize name part = (sanitize name) + (match part with + | ReflectionMemberPart -> "$" + reflectionSuffix | InstanceMemberPart(s, i) -> printPart sanitize "$$" s i | StaticMemberPart(s, i) -> printPart sanitize "$$$" s i | NoMemberPart -> "") diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index 7aa81d3420..ba9d671296 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -1188,6 +1188,16 @@ let precompiledLib r (t: Type) (i: CallInfo) (thisArg: Expr option) (args: Expr let argInfo = { argInfo thisArg args (Typed i.SignatureArgTypes) with Spread = i.Spread } makeCustomImport Any mangledName importPath |> staticCall r t argInfo +let getPrecompiledLibReflectionName entityName = + let entityName = Naming.sanitizeIdentForbiddenChars entityName + Naming.buildNameWithoutSanitation entityName Naming.ReflectionMemberPart |> Naming.checkJsKeywords + +let precompiledLibReflection r (args: Expr list) (entityName, importPath) = + let mangledName = getPrecompiledLibReflectionName entityName + let argTypes = args |> List.map (fun _ -> MetaType) + let argInfo = { argInfo None args (Typed argTypes) with Spread = Fable.NoSpread } + makeCustomImport Any mangledName importPath |> staticCall r MetaType argInfo + let fsFormat (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) = match i.CompiledName, thisArg, args with | "get_Value", Some callee, _ -> diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 9576f1f511..57a6e60955 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -3,6 +3,16 @@ import { compareArraysWith, equalArraysWith, stringHash } from "./Util"; // tslint:disable: max-line-length +export enum NReflKind { + Property = 0, + Field = 1, + MethodBase = 2, + Method = 3, + Constructor = 4, + UnionCase = 5, + Type = 6, +} + export class NParameterInfo { constructor( public Name: string, @@ -41,7 +51,13 @@ export abstract class NMemberInfo { this.attributes = att || []; } + public abstract getMemberKind(): NReflKind; public abstract toPrettyString(): string; + + public is(kind: NReflKind) { + return this.getMemberKind() === kind; + } + public get_Name() { return this.Name; } public get_DeclaringType() { return this.DeclaringType; } @@ -134,6 +150,10 @@ export class NMethodInfo extends NMethodBase { } + public getMemberKind() { + return NReflKind.Method; + } + public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genMap) { @@ -234,6 +254,10 @@ export class NConstructorInfo extends NMethodBase { return attPrefix + "new(" + args + ")"; } + public getMemberKind() { + return NReflKind.Constructor; + } + public toString() { return this.toPrettyString(); } @@ -263,6 +287,10 @@ export class NFieldInfo extends NMemberInfo { public get_FieldType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } + public getMemberKind() { + return NReflKind.Field; + } + public toPrettyString() { const typ = this.Type.toFullString(); let prefix = "val "; @@ -309,6 +337,10 @@ export class NPropertyInfo extends NMemberInfo { this.Type = type.get_ContainsGenericParameters() ? DeclaringType.ResolveGeneric(type) : type; } + + public getMemberKind() { + return NReflKind.Property; + } public get_PropertyType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } public get_IsFSharp() { return this.IsFSharp; } @@ -328,7 +360,7 @@ export class NPropertyInfo extends NMemberInfo { public get_GetMethod() { const getterName = "get_" + this.Name; const mems = this.DeclaringType.GetAllMembers(); - const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); + const idx = mems.findIndex((m) => isMethodInfo(m) && m.Name === getterName); if (idx >= 0) { return mems[idx] as NMethodInfo; } else { @@ -339,7 +371,7 @@ export class NPropertyInfo extends NMemberInfo { public get_SetMethod() { const getterName = "set_" + this.Name; const mems = this.DeclaringType.GetAllMembers(); - const idx = mems.findIndex((m) => m instanceof NMethodInfo && m.Name === getterName); + const idx = mems.findIndex((m) => isMethodInfo(m) && m.Name === getterName); if (idx >= 0) { return mems[idx] as NMethodInfo; } else { return null; } } @@ -436,6 +468,11 @@ export class NUnionCaseInfo extends NMemberInfo { }); } + + public getMemberKind() { + return NReflKind.UnionCase; + } + public GetFields() { return this.Fields; } @@ -627,25 +664,25 @@ export class NTypeInfo { public GetMembers() { const m = this.GetAllMembers(); - return m.filter((m) => !(m instanceof NUnionCaseInfo)); + return m.filter((m) => !(isUnionCaseInfo(m))); } public GetProperties() { const m = this.GetAllMembers(); - return m.filter((m) => m instanceof NPropertyInfo) as NPropertyInfo[]; + return m.filter((m) => isPropertyInfo(m)) as NPropertyInfo[]; } public GetMethods() { const m = this.GetAllMembers(); - return m.filter((m) => m instanceof NMethodInfo) as NMethodInfo[]; + return m.filter((m) => isMethodInfo(m)) as NMethodInfo[]; } public GetConstructors() { const m = this.GetAllMembers(); - return m.filter((m) => m instanceof NConstructorInfo) as NConstructorInfo[]; + return m.filter((m) => isConstructorInfo(m)) as NConstructorInfo[]; } public GetFields() { const m = this.GetAllMembers(); - return m.filter((m) => m instanceof NFieldInfo) as NFieldInfo[]; + return m.filter((m) => isFieldInfo(m)) as NFieldInfo[]; } public GetConstructor(ts?: NTypeInfo[]) { if (ts) { @@ -657,21 +694,21 @@ export class NTypeInfo { } public GetField(name: string) { const m = this.GetAllMembers(); - return m.find((m) => m instanceof NFieldInfo && m.Name === name) as NFieldInfo; + return m.find((m) => isFieldInfo(m) && m.Name === name) as NFieldInfo; } public GetProperty(name: string) { const m = this.GetAllMembers(); - const prop = m.find((m) => m instanceof NPropertyInfo && m.Name === name) as NPropertyInfo; + const prop = m.find((m) => isPropertyInfo(m) && m.Name === name) as NPropertyInfo; return prop; } public GetMethod(name: string, types?: NTypeInfo[]) { const m = this.GetAllMembers(); if (types) { - const meths = m.filter((m) => m instanceof NMethodInfo && m.Name === name && m.ParametersAssignable(types)) as NMethodInfo[]; + const meths = m.filter((m) => isMethodInfo(m) && m.Name === name && (m as NMethodInfo).ParametersAssignable(types)) as NMethodInfo[]; return meths.length === 1 ? meths[0] : null; } else { - const meths = m.filter((m) => m instanceof NMethodInfo && m.Name === name) as NMethodInfo[]; + const meths = m.filter((m) => isMethodInfo(m) && m.Name === name) as NMethodInfo[]; return meths.length === 1 ? meths[0] : null; } } @@ -703,7 +740,7 @@ export class NTypeInfo { const members = this .GetMembers() - .filter((m) => !(m instanceof NMethodInfo) || !(m.Name.startsWith("get_") || m.Name.startsWith("set_"))) + .filter((m) => !(isMethodInfo(m)) || !(m.Name.startsWith("get_") || m.Name.startsWith("set_"))) .map((m) => " " + m.toPrettyString()).join("\n"); return "type " + this.toFullString() + "=\n" + members; } @@ -887,80 +924,39 @@ export const float32: NTypeInfo = NTypeInfo.Simple("System.Single"); export const float64: NTypeInfo = NTypeInfo.Simple("System.Double"); export const decimal: NTypeInfo = NTypeInfo.Simple("System.Decimal"); -// export function name(info: FieldInfo | CaseInfo | TypeInfo): string { -// if (Array.isArray(info)) { -// return info[0]; -// } else if (info instanceof CaseInfo) { -// return info.name; -// } else { -// const i = info.fullname.lastIndexOf("."); -// return i === -1 ? info.fullname : info.fullname.substr(i + 1); -// } -// } - -// export function fullName(t: TypeInfo): string { -// const gen = t.generics != null && !isArray(t) ? t.generics : []; -// if (gen.length > 0) { -// return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]"; -// } else { -// return t.fullname; -// } -// } - -// export function namespace(t: TypeInfo) { -// const i = t.fullname.lastIndexOf("."); -// return i === -1 ? "" : t.fullname.substr(0, i); -// } - -// export function isArray(t: TypeInfo): boolean { -// return t.fullname.endsWith("[]"); -// } - -// export function getElementType(t: TypeInfo): TypeInfo { -// return isArray(t) ? t.generics[0] : null; -// } - -// export function isGenericType(t: TypeInfo) { -// return t.generics != null && t.generics.length > 0; -// } - -// /** -// * This doesn't replace types for fields (records) or cases (unions) -// * but it should be enough for type comparison purposes -// */ -// export function getGenericTypeDefinition(t: TypeInfo) { -// return t.generics == null ? t : new TypeInfo(t.fullname, t.generics.map(() => obj)); -// } - -// FSharpType export function isType(o: any) { - return o instanceof NTypeInfo; + return "fullname" in o; } export function isMemberInfo(o: any) { - return o instanceof NMemberInfo; + return "getMemberKind" in o; } export function isMethodBase(o: any) { - return o instanceof NMethodBase; + if (isMemberInfo(o)) { + const k = o.getMemberKind(); + return k === NReflKind.Method || k === NReflKind.Constructor; + } else { + return false; + } } export function isMethodInfo(o: any) { - return o instanceof NMethodInfo; + return isMemberInfo(o) && o.getMemberKind() === NReflKind.Method; } export function isPropertyInfo(o: any) { - return o instanceof NPropertyInfo; + return isMemberInfo(o) && o.getMemberKind() === NReflKind.Property; } export function isUnionCaseInfo(o: any) { - return o instanceof NUnionCaseInfo; + return isMemberInfo(o) && o.getMemberKind() === NReflKind.UnionCase; } export function isFieldInfo(o: any) { - return o instanceof NFieldInfo; + return isMemberInfo(o) && o.getMemberKind() === NReflKind.Field; } export function isConstructorInfo(o: any) { - return o instanceof NConstructorInfo; + return isMemberInfo(o) && o.getMemberKind() === NReflKind.Constructor; } export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { - const cases = t.GetAllMembers().filter((m) => m instanceof NUnionCaseInfo) as NUnionCaseInfo[]; + const cases = t.GetAllMembers().filter((m) => isUnionCaseInfo(m)) as NUnionCaseInfo[]; if (cases.length > 0) { return cases; } else { @@ -969,7 +965,7 @@ export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { } export function getRecordElements(t: NTypeInfo): NPropertyInfo[] { - const fields = t.GetAllMembers().filter((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; + const fields = t.GetAllMembers().filter((m) => isPropertyInfo(m) && (m as NPropertyInfo).IsFSharp) as NPropertyInfo[]; if (fields.length > 0) { return fields; } else { @@ -995,8 +991,8 @@ export function getFunctionElements(t: NTypeInfo): [NTypeInfo, NTypeInfo] { } export function isUnion(t: any): boolean { - if (t instanceof NTypeInfo) { - const idx = (t as NTypeInfo).GetAllMembers().findIndex((m) => m instanceof NUnionCaseInfo); + if (isType(t)) { + const idx = (t as NTypeInfo).GetAllMembers().findIndex((m) => isUnionCaseInfo(m)); return idx >= 0; } else { return t instanceof Union; @@ -1004,8 +1000,8 @@ export function isUnion(t: any): boolean { } export function isRecord(t: any): boolean { - if (t instanceof NTypeInfo) { - const idx = t.GetAllMembers().findIndex((m) => m instanceof NPropertyInfo && (m as NPropertyInfo).IsFSharp); + if (isType(t)) { + const idx = (t as NTypeInfo).GetAllMembers().findIndex((m) => isPropertyInfo(m) && (m as NPropertyInfo).IsFSharp); return idx >= 0; } else { return t instanceof Record; @@ -1089,7 +1085,7 @@ export function makeRecord(t: NTypeInfo, values: any[]): any { if (fields.length !== values.length) { throw new Error(`Expected an array of length ${fields.length} but got ${values.length}`); } - const ctor = t.GetAllMembers().find((m) => m instanceof NConstructorInfo) as NConstructorInfo; + const ctor = t.GetAllMembers().find((m) => isConstructorInfo(m)) as NConstructorInfo; return ctor.Invoke(values); } From 185f57a4b77bd26eab6a702218f5f75d8a52c89b Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 29 Apr 2019 09:39:16 +0200 Subject: [PATCH 32/38] Reflection: added GetHashCode/Equals/CompareTo to MemberInfos --- src/Fable.Transforms/Replacements.fs | 29 +++ src/fable-library/Reflection.ts | 307 +++++++++++++++++++++++++-- 2 files changed, 319 insertions(+), 17 deletions(-) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index ba9d671296..1bc1f189fb 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -210,6 +210,13 @@ let (|Nameof|_|) = function let (|ReplaceName|_|) (namesAndReplacements: (string*string) list) name = namesAndReplacements |> List.tryPick (fun (name2, replacement) -> if name2 = name then Some replacement else None) +let (|DeclaredFullNameType|_|) = function + | DeclaredType(ent, targs) -> + match ent.TryFullName with + | Some name -> Some (ent, name, targs) + | _ -> None + | _ -> + None let inline (|ExprType|) (e: Expr) = e.Type @@ -736,6 +743,28 @@ let rec equals (com: ICompiler) r equal (left: Expr) (right: Expr) = Helper.CoreCall("Util", "equals", Boolean, [left; right], ?loc=r) |> is equal | MetaType -> Helper.CoreCall("Reflection", "equals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, ("System.Reflection.MemberInfo" | "System.Reflection.MethodBase"), []) -> + Helper.CoreCall("Reflection", "memberEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "System.Reflection.ParameterInfo", []) -> + Helper.CoreCall("Reflection", "parameterEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "System.Reflection.FieldInfo", []) -> + Helper.CoreCall("Reflection", "fieldEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "System.Reflection.PropertyInfo", []) -> + Helper.CoreCall("Reflection", "propertyEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "System.Reflection.MethodInfo", []) -> + Helper.CoreCall("Reflection", "methodEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "System.Reflection.ConstructorInfo", []) -> + Helper.CoreCall("Reflection", "constructorEquals", Boolean, [left; right], ?loc=r) |> is equal + + | DeclaredFullNameType(_ent, "Microsoft.FSharp.Reflection.UnionCaseInfo", []) -> + Helper.CoreCall("Reflection", "unionCaseEquals", Boolean, [left; right], ?loc=r) |> is equal + | Tuple _ -> Helper.CoreCall("Util", "equalArrays", Boolean, [left; right], ?loc=r) |> is equal // unsafe optimization, left can sometimes be null diff --git a/src/fable-library/Reflection.ts b/src/fable-library/Reflection.ts index 57a6e60955..ea8046b590 100644 --- a/src/fable-library/Reflection.ts +++ b/src/fable-library/Reflection.ts @@ -1,5 +1,5 @@ import { anonRecord as makeAnonRecord, Record, Union } from "./Types"; -import { compareArraysWith, equalArraysWith, stringHash } from "./Util"; +import { combineHashCodes, compareArraysWith, equalArraysWith, stringHash } from "./Util"; // tslint:disable: max-line-length @@ -13,6 +13,11 @@ export enum NReflKind { Type = 6, } +function compareStrings(l: string, r: string) { + if (l === r) { return 0; } + return l < r ? -1 : 1; +} + export class NParameterInfo { constructor( public Name: string, @@ -35,6 +40,29 @@ export class NParameterInfo { return this.Name; } + public GetHashCode() { + return combineHashCodes([stringHash(this.Name), this.ParameterType.GetHashCode()]); + } + + public Equals(o: any) { + if ("Name" in o && "ParameterType" in o) { + return this.Name === o.Name && equals(this.ParameterType, o.ParameterType); + } else { + return false; + } + } + + public CompareTo(o: any) { + if (o == null) { throw new Error("cannot compare to null"); } + + if ("Name" in o && "ParameterType" in o) { + const c = compareStrings(this.Name, o.Name); + if (c !== 0) { return c; } + return this.ParameterType.CompareTo(o.ParameterType); + } + throw new Error(`cannot compare to ${o}`); + } + } export abstract class NMemberInfo { @@ -54,6 +82,10 @@ export abstract class NMemberInfo { public abstract getMemberKind(): NReflKind; public abstract toPrettyString(): string; + public abstract GetHashCode(): number; + public abstract Equals(o: any): boolean; + public abstract CompareTo(o: any): number; + public is(kind: NReflKind) { return this.getMemberKind() === kind; } @@ -154,6 +186,36 @@ export class NMethodInfo extends NMethodBase { return NReflKind.Method; } + public GetHashCode() { + return combineHashCodes([ + this.DeclaringType.GetHashCode(), + combineHashCodes(this.GenericArguments.map((a) => a.GetHashCode())), + stringHash(this.Name), + combineHashCodes(this.Parameters.map((a) => a.GetHashCode())), + (this.IsStatic ? 1 : 0), + ]); + } + + public CompareTo(oo: any) { + if (!isMethodInfo(oo)) { throw new Error(`cannot compare MethodInfo to ${oo}`); } + const o = oo as NMethodInfo; + let c = 0; + c = compareStrings(this.Name, o.Name); if (c !== 0) { return c; } + c = this.IsStatic === o.IsStatic ? 0 : (this.IsStatic < o.IsStatic ? -1 : 1); if (c !== 0) { return c; } + c = this.DeclaringType.CompareTo(o.DeclaringType); if (c !== 0) { return c; } + c = compareArraysWith(this.GenericArguments, o.GenericArguments, (l, r) => l.CompareTo(r)); if (c !== 0) { return c; } + c = compareArraysWith(this.Parameters, o.Parameters, (l, r) => l.CompareTo(r)); if (c !== 0) { return c; } + return 0; + } + + public Equals(ob: any) { + if (isMethodInfo(ob)) { + return methodEquals(this, ob as NMethodInfo); + } else { + return false; + } + } + public ResolveGeneric(t: NTypeInfo): NTypeInfo { if (t.isGenericParameter) { if (t.fullname in this.genMap) { @@ -258,6 +320,30 @@ export class NConstructorInfo extends NMethodBase { return NReflKind.Constructor; } + public GetHashCode() { + return combineHashCodes([ + this.DeclaringType.GetHashCode(), + combineHashCodes(this.Parameters.map((p) => p.GetHashCode())), + ]); + } + + public Equals(o: any) { + if (isConstructorInfo(o)) { + return constructorEquals(this, o as NConstructorInfo); + } else { + return false; + } + } + + public CompareTo(oo: any) { + if (!isConstructorInfo(oo)) { throw new Error(`cannot compare ConstructorInfo to ${oo}`); } + const o = oo as NConstructorInfo; + let c = 0; + c = this.DeclaringType.CompareTo(o.DeclaringType); if (c !== 0) { return c; } + c = compareArraysWith(this.Parameters, o.Parameters, (l, r) => l.CompareTo(r)); if (c !== 0) { return c; } + return 0; + } + public toString() { return this.toPrettyString(); } @@ -291,6 +377,32 @@ export class NFieldInfo extends NMemberInfo { return NReflKind.Field; } + public GetHashCode() { + return combineHashCodes([ + stringHash(this.Name), + this.DeclaringType.GetHashCode(), + (this.IsStatic ? 1 : 0), + ]); + } + + public Equals(o: any) { + if (isFieldInfo(o)) { + return fieldEquals(this, o as NFieldInfo); + } else { + return false; + } + } + + public CompareTo(oo: any) { + if (!isFieldInfo(oo)) { throw new Error(`cannot compare FieldInfo to ${oo}`); } + const o = oo as NFieldInfo; + let c = 0; + c = compareStrings(this.Name, o.Name); if (c !== 0) { return c; } + c = this.IsStatic === o.IsStatic ? 0 : (this.IsStatic < o.IsStatic ? -1 : 1); if (c !== 0) { return c; } + c = this.DeclaringType.CompareTo(o.DeclaringType); if (c !== 0) { return c; } + return 0; + } + public toPrettyString() { const typ = this.Type.toFullString(); let prefix = "val "; @@ -341,6 +453,35 @@ export class NPropertyInfo extends NMemberInfo { public getMemberKind() { return NReflKind.Property; } + + public GetHashCode() { + return combineHashCodes([ + stringHash(this.Name), + this.DeclaringType.GetHashCode(), + (this.IsFSharp ? 1 : 0), + (this.IsStatic ? 1 : 0), + ]); + } + + public Equals(o: any) { + if (isPropertyInfo(o)) { + return propertyEquals(this, o as NPropertyInfo); + } else { + return false; + } + } + + public CompareTo(oo: any) { + if (!isPropertyInfo(oo)) { throw new Error(`cannot compare FieldInfo to ${oo}`); } + const o = oo as NPropertyInfo; + let c = 0; + c = this.IsStatic === o.IsStatic ? 0 : (this.IsStatic < o.IsStatic ? -1 : 1); if (c !== 0) { return c; } + c = this.IsFSharp === o.IsFSharp ? 0 : (this.IsFSharp < o.IsFSharp ? -1 : 1); if (c !== 0) { return c; } + c = compareStrings(this.Name, o.Name); if (c !== 0) { return c; } + c = this.DeclaringType.CompareTo(o.DeclaringType); if (c !== 0) { return c; } + return 0; + } + public get_PropertyType() { return this.Type; } public get_IsStatic() { return this.IsStatic; } public get_IsFSharp() { return this.IsFSharp; } @@ -468,11 +609,35 @@ export class NUnionCaseInfo extends NMemberInfo { }); } - public getMemberKind() { return NReflKind.UnionCase; } + public GetHashCode() { + return combineHashCodes([ + stringHash(this.Name), + this.DeclaringType.GetHashCode(), + ]); + } + + public Equals(o: any) { + if (isUnionCaseInfo(o)) { + return unionCaseEquals(this, o as NUnionCaseInfo); + } else { + return false; + } + } + + public CompareTo(oo: any) { + if (!isUnionCaseInfo(oo)) { throw new Error(`cannot compare UnionCaseInfo to ${oo}`); } + const o = oo as NUnionCaseInfo; + let c = 0; + c = compareStrings(this.Name, o.Name); if (c !== 0) { return c; } + c = this.DeclaringType.CompareTo(o.DeclaringType); if (c !== 0) { return c; } + return 0; + } + + public GetFields() { return this.Fields; } @@ -790,18 +955,127 @@ export function getGenerics(t: NTypeInfo): NTypeInfo[] { } export function equals(t1: NTypeInfo, t2: NTypeInfo): boolean { - if (t1.fullname === t2.fullname) { - const g1 = getGenerics(t1); - const g2 = getGenerics(t2); - try { - return equalArraysWith(g1, g2, equals); - } catch (e) { - throw new Error(t1.fullname + " g1: " + g1 + " g2: " + g2); + if (t1 === t2) { return true; } else + if (t1 == null && t2 != null) { return false; } else + if (t1 != null && t2 == null) { return false; } else { + if (t1.fullname === t2.fullname) { + const g1 = getGenerics(t1); + const g2 = getGenerics(t2); + try { + return equalArraysWith(g1, g2, equals); + } catch (e) { + throw new Error(t1.fullname + " g1: " + g1 + " g2: " + g2); + } + } else { + return false; + } + } +} + +function typesEqual(l: NTypeInfo[], r: NTypeInfo[]) { + if (l.length === r.length) { + for (let i = 0; i < l.length; i++) { + if (!equals(l[i], r[i])) { return false; } } + return true; } else { return false; } } +export function parameterEquals(l: NParameterInfo, r: NParameterInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return l.Name === r.Name && equals(l.ParameterType, r.ParameterType); + } +} + +function parametersEqual(l: NParameterInfo[], r: NParameterInfo[]) { + if (l.length === r.length) { + for (let i = 0; i < l.length; i++) { + if (!parameterEquals(l[i], r[i])) { return false; } + } + return true; + } else { + return false; + } +} + +export function fieldEquals(l: NFieldInfo, r: NFieldInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return l.Name === r.Name && l.IsStatic === r.IsStatic && equals(l.DeclaringType, r.DeclaringType); + } +} + +export function propertyEquals(l: NPropertyInfo, r: NPropertyInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return l.Name === r.Name && l.IsFSharp === r.IsFSharp && l.IsStatic === r.IsStatic && equals(l.DeclaringType, r.DeclaringType); + } +} +function propertiesEqual(l: NPropertyInfo[], r: NPropertyInfo[]) { + if (l.length === r.length) { + for (let i = 0; i < l.length; i++) { + if (!propertyEquals(l[i], r[i])) { return false; } + } + return true; + } else { + return false; + } +} +export function constructorEquals(l: NConstructorInfo, r: NConstructorInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return equals(l.DeclaringType, r.DeclaringType) && parametersEqual(l.Parameters, r.Parameters); + } +} + +export function methodEquals(l: NMethodInfo, r: NMethodInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return l.Name === r.Name && + l.IsStatic === r.IsStatic && + equals(l.DeclaringType, r.DeclaringType) && + typesEqual(l.GenericArguments, r.GenericArguments) && + parametersEqual(l.Parameters, r.Parameters); + } +} + +export function unionCaseEquals(l: NUnionCaseInfo, r: NUnionCaseInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + return l.Name === r.Name && equals(l.DeclaringType, r.DeclaringType); + } +} + +export function memberEquals(l: NMemberInfo, r: NMemberInfo) { + if (l === r) { return true; } else + if (l == null && r != null) { return false; } else + if (l != null && r == null) { return false; } else { + const lk = l.getMemberKind(); + const rk = r.getMemberKind(); + if (lk !== rk) { return false; } + + switch (lk) { + case NReflKind.Constructor: return constructorEquals(l as NConstructorInfo, r as NConstructorInfo); + case NReflKind.Method: return methodEquals(l as NMethodInfo, r as NMethodInfo); + case NReflKind.Field: return fieldEquals(l as NFieldInfo, r as NFieldInfo); + case NReflKind.Property: return propertyEquals(l as NPropertyInfo, r as NPropertyInfo); + case NReflKind.UnionCase: return unionCaseEquals(l as NUnionCaseInfo, r as NUnionCaseInfo); + default: return l === r; + } + } +} + +export function methodBaseEquals(l: NMemberInfo, r: NMemberInfo) { + return memberEquals(l, r); +} // System.Type is not comparable in .NET, but let's implement this // in case users want to create a dictionary with types as keys @@ -924,12 +1198,11 @@ export const float32: NTypeInfo = NTypeInfo.Simple("System.Single"); export const float64: NTypeInfo = NTypeInfo.Simple("System.Double"); export const decimal: NTypeInfo = NTypeInfo.Simple("System.Decimal"); - export function isType(o: any) { - return "fullname" in o; + return o != null && "fullname" in o; } export function isMemberInfo(o: any) { - return "getMemberKind" in o; + return o != null && "getMemberKind" in o; } export function isMethodBase(o: any) { if (isMemberInfo(o)) { @@ -940,19 +1213,19 @@ export function isMethodBase(o: any) { } } export function isMethodInfo(o: any) { - return isMemberInfo(o) && o.getMemberKind() === NReflKind.Method; + return o != null && isMemberInfo(o) && o.getMemberKind() === NReflKind.Method; } export function isPropertyInfo(o: any) { - return isMemberInfo(o) && o.getMemberKind() === NReflKind.Property; + return o != null && isMemberInfo(o) && o.getMemberKind() === NReflKind.Property; } export function isUnionCaseInfo(o: any) { - return isMemberInfo(o) && o.getMemberKind() === NReflKind.UnionCase; + return o != null && isMemberInfo(o) && o.getMemberKind() === NReflKind.UnionCase; } export function isFieldInfo(o: any) { - return isMemberInfo(o) && o.getMemberKind() === NReflKind.Field; + return o != null && isMemberInfo(o) && o.getMemberKind() === NReflKind.Field; } export function isConstructorInfo(o: any) { - return isMemberInfo(o) && o.getMemberKind() === NReflKind.Constructor; + return o != null && isMemberInfo(o) && o.getMemberKind() === NReflKind.Constructor; } export function getUnionCases(t: NTypeInfo): NUnionCaseInfo[] { From 707661bbcd85ac9909b4dba0c944f0a8edd24f22 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Mon, 29 Apr 2019 09:40:04 +0200 Subject: [PATCH 33/38] Quotations: Var now using Guid instead of incrementing id (caused problems in REPL) --- src/fable-library/Quotations.fs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/fable-library/Quotations.fs b/src/fable-library/Quotations.fs index c5623c14d3..be53ddae97 100644 --- a/src/fable-library/Quotations.fs +++ b/src/fable-library/Quotations.fs @@ -68,26 +68,16 @@ open Helpers [] [] -[] type Var(name: string, typ:Type, ?isMutable: bool) = - inherit obj() - - static let getStamp = - let mutable lastStamp = -1L // first value retrieved will be 0 - fun () -> - let s = lastStamp - lastStamp <- s + -1L - s - static let globals = new Dictionary<(string*Type), Var>(11) - let stamp = getStamp () + let id = System.Guid.NewGuid() let isMutable = defaultArg isMutable false member v.Name = name member v.IsMutable = isMutable member v.Type = typ - member v.Stamp = stamp + member x.Stamp = id static member Global(name, typ: Type) = checkNonNull "name" name @@ -100,12 +90,14 @@ type Var(name: string, typ:Type, ?isMutable: bool) = 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 + override x.GetHashCode() = + Unchecked.hash id + + override x.Equals o = + match o with + | :? Var as v -> id = v.Stamp + | _ -> false interface System.IComparable with member v.CompareTo(obj:obj) = From 2b8d6fddf5ff04903b96b2027ca8ec2cd2a6c8d8 Mon Sep 17 00:00:00 2001 From: Alfonso Garcia-Caro Date: Thu, 9 May 2019 19:19:08 +0200 Subject: [PATCH 34/38] Publish fable-compiler-quotations 2.3.7 --- src/fable-compiler/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fable-compiler/package.json b/src/fable-compiler/package.json index 650fbb3246..10c7c6af35 100644 --- a/src/fable-compiler/package.json +++ b/src/fable-compiler/package.json @@ -1,5 +1,5 @@ { - "name": "fable-compiler", + "name": "fable-compiler-quotations", "version": "2.3.7", "main": "dist/index.js", "description": "Fable compiler", From a7495d6135c60920be840540f92eaa9159205941 Mon Sep 17 00:00:00 2001 From: Alfonso Garcia-Caro Date: Mon, 20 May 2019 10:46:36 +0200 Subject: [PATCH 35/38] Fix fable-library --- src/fable-library/ExprUtils.fs | 84 ++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/src/fable-library/ExprUtils.fs b/src/fable-library/ExprUtils.fs index f0b4c13b50..1290757d41 100644 --- a/src/fable-library/ExprUtils.fs +++ b/src/fable-library/ExprUtils.fs @@ -1,11 +1,15 @@ -module ExprUtils +module ExprUtils open Microsoft.FSharp.Quotations open Fable.Core -open Fable.Import.JS +open Fable.Core.JS +open Fable.Core.JsInterop open System.Reflection open FSharp.Collections +[] +let private createUint8Array(buffer: ArrayBuffer, byteOffset: int, byteLength: int): byte[] = jsNative + type IValue = abstract member typ : System.Type abstract member value : obj @@ -21,8 +25,8 @@ type ILiteral = abstract member value : obj -type BinaryStream(arr : Uint8Array) = - let view = DataView.Create(arr.buffer, arr.byteOffset, arr.byteLength) +type BinaryStream(arr : byte[]) = + let view = DataView.Create(arr.buffer, arr?byteOffset, arr?byteLength) let mutable position = 0 member x.Position = position @@ -41,7 +45,7 @@ type BinaryStream(arr : Uint8Array) = code, None member x.ReadInt32() = - let value = view.getInt32(float position, true) + let value = view.getInt32(position, true) position <- position + 4 unbox value @@ -56,7 +60,7 @@ type BinaryStream(arr : Uint8Array) = member x.ReadString() = let length = x.ReadInt32() - let view = Uint8Array.Create(arr.buffer, arr.byteOffset + float position, float length) + let view = createUint8Array(arr.buffer, arr?byteOffset + position, length) let value = System.Text.Encoding.UTF8.GetString(unbox view) position <- position + length value @@ -103,7 +107,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty else let h = f i h :: init (i + 1) - init 0 + init 0 let rec read () = @@ -113,7 +117,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty | Some(sl, sc, el, ec) -> e.WithRange(file, sl, sc, el, ec) | None -> e match tag with - | 1uy -> + | 1uy -> let vid = stream.ReadInt32() let body = read() Expr.Lambda(variables.[vid], body) |> withRange @@ -124,10 +128,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let vid = stream.ReadInt32() values.[vid] |> withRange | 4uy -> - let vid = stream.ReadInt32() + let vid = stream.ReadInt32() let e = read() let b = read() - Expr.Let(variables.[vid], e, b) |> withRange + Expr.Let(variables.[vid], e, b) |> withRange | 5uy -> let decl = types.[stream.ReadInt32()] @@ -142,10 +146,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let args = init cnt (fun _ -> read()) let mem = - decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> + decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> m.Name = name && m.GetParameters().Length = cnt && m.GetGenericArguments().Length = margs.Length && - FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) + FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) (if m.IsGenericMethod then m.MakeGenericMethod(margs).GetParameters() else m.GetParameters()) (List.toArray args) ) @@ -172,10 +176,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let args = init cnt (fun _ -> read()) let mem = - decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> + decl.GetMethods() |> FSharp.Collections.Array.tryFind (fun m -> m.Name = name && m.GetParameters().Length = cnt && m.GetGenericArguments().Length = margs.Length && - FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) + FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) (a : Expr) -> p.ParameterType = a.Type) (if m.IsGenericMethod then m.MakeGenericMethod(margs).GetParameters() else m.GetParameters()) (List.toArray args) ) @@ -188,7 +192,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty Expr.Call(mem, args) |> withRange | None -> let mem = createMethod decl name mpars margs dargs ret true - Expr.Call(mem, args) |> withRange + Expr.Call(mem, args) |> withRange | 7uy -> let e = read() @@ -197,15 +201,15 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty | 8uy -> let v = read() let e = read() - Expr.AddressSet(v, e) |> withRange + Expr.AddressSet(v, e) |> withRange | 9uy -> - let tid = stream.ReadInt32() + let tid = stream.ReadInt32() let name = stream.ReadString() let target = read() //let prop = FSharp.Reflection.FSharpType.GetRecordFields target.Type |> FSharp.Collections.Array.find (fun p -> p.Name = name) let prop = createRecordProperty target.Type name types.[tid] - Expr.PropertyGet(target, prop) |> withRange + Expr.PropertyGet(target, prop) |> withRange | 10uy -> let f = read() @@ -217,9 +221,9 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let l = literals.[id] Expr.Value(l.value, l.typ) |> withRange | 12uy -> - let c = read() - let i = read() - let e = read() + let c = read() + let i = read() + let e = read() Expr.IfThenElse(c, i, e) |> withRange | 13uy -> @@ -261,14 +265,14 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let idx = init cidx (fun _ -> read()) let ret = types.[stream.ReadInt32()] let target = read() - + let prop = typ.GetProperties() |> FSharp.Collections.Array.tryFind (fun p -> p.Name = name && p.PropertyType = ret) match prop with | Some prop -> Expr.PropertyGet(target, prop, idx) |> withRange | None -> let prop = createRecordProperty typ name ret - Expr.PropertyGet(target, prop, idx) |> withRange + Expr.PropertyGet(target, prop, idx) |> withRange | 19uy -> let typ = types.[stream.ReadInt32()] @@ -283,7 +287,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty Expr.PropertyGet(prop, idx) |> withRange | None -> let prop = createStaticProperty typ name ret - Expr.PropertyGet(prop, idx) |> withRange + Expr.PropertyGet(prop, idx) |> withRange | 20uy -> let typ = types.[stream.ReadInt32()] @@ -316,11 +320,11 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty Expr.PropertySet(prop, value, idx) |> withRange | None -> let prop = createStaticProperty typ name ret - Expr.PropertySet(prop, value, idx) |> withRange + Expr.PropertySet(prop, value, idx) |> withRange | 22uy -> let cnt = stream.ReadInt32() - let bindings = + let bindings = init cnt (fun _ -> let v = variables.[stream.ReadInt32()] let e = read() @@ -340,10 +344,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let argts = stream.ReadInt32Array() |> FSharp.Collections.Array.map (fun t -> types.[t]) let args = init argts.Length (fun _ -> read()) - let ctor = - typ.GetConstructors() - |> FSharp.Collections.Array.tryFind (fun ctor -> - ctor.GetParameters().Length = argts.Length && + let ctor = + typ.GetConstructors() + |> FSharp.Collections.Array.tryFind (fun ctor -> + ctor.GetParameters().Length = argts.Length && FSharp.Collections.Array.forall2 (fun (p : ParameterInfo) t -> p.ParameterType = t) (ctor.GetParameters()) argts ) @@ -351,21 +355,21 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty | Some ctor -> Expr.NewObject(ctor, args) |> withRange | _ -> - failwith "no ctor found" + failwith "no ctor found" | 27uy -> - let typ = types.[stream.ReadInt32()] + let typ = types.[stream.ReadInt32()] let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) Expr.NewRecord(typ, args) |> withRange - + | 28uy -> let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) Expr.NewTuple(args) |> withRange - + | 29uy -> - let typ = types.[stream.ReadInt32()] + let typ = types.[stream.ReadInt32()] let name = stream.ReadString() let cnt = stream.ReadInt32() let args = init cnt (fun _ -> read()) @@ -387,7 +391,7 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty Expr.TupleGet(t, i) |> withRange | 33uy -> - let typ = types.[stream.ReadInt32()] + let typ = types.[stream.ReadInt32()] let target = read() Expr.TypeTest(target, typ) |> withRange | 36uy -> @@ -403,10 +407,10 @@ let deserialize (values : IValue[]) (variables : IVariable[]) (types : System.Ty let str = stream.ReadString() match range with | Some (sl, sc, _el, _ec) -> - failwithf "%s [%d, %d]: unsupported expression: %s" file sl sc str - | None -> - failwithf "%s: unsupported expression: %s" file str + failwithf "%s [%d, %d]: unsupported expression: %s" file sl sc str + | None -> + failwithf "%s: unsupported expression: %s" file str | _ -> - failwithf "invalid expression: %A at %A" tag stream.Position + failwithf "invalid expression: %A at %A" tag stream.Position read() From bf257305078925b6365e3cd808323a941ecbfce6 Mon Sep 17 00:00:00 2001 From: Alfonso Garcia-Caro Date: Mon, 20 May 2019 11:20:43 +0200 Subject: [PATCH 36/38] Publish 2.3.10 --- src/fable-compiler/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fable-compiler/package.json b/src/fable-compiler/package.json index 10c7c6af35..0bbb57ea3d 100644 --- a/src/fable-compiler/package.json +++ b/src/fable-compiler/package.json @@ -1,6 +1,6 @@ { "name": "fable-compiler-quotations", - "version": "2.3.7", + "version": "2.3.10", "main": "dist/index.js", "description": "Fable compiler", "keywords": [ From cd1cb8fcb89ad2027814dd1817bc143356ff5425 Mon Sep 17 00:00:00 2001 From: krauthaufen Date: Fri, 31 May 2019 21:31:59 +0200 Subject: [PATCH 37/38] [FSharpToFable] hacky fix for unknown attribute constructors (solving https://github.com/fable-compiler/Fable/pull/1839#issuecomment-496550295) --- src/Fable.Transforms/FSharp2Fable.fs | 36 ++++++++++++++++++++----- src/Fable.Transforms/Fable2Babel.fs | 2 ++ src/Fable.Transforms/Global/Compiler.fs | 1 + src/Fable.Transforms/State.fs | 7 +++++ tests/Main/ReflectionTests.fs | 1 + 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 55452e89bb..d1bece643c 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -281,11 +281,12 @@ let rec private transformDecisionTargets (com: IFableCompiler) (ctx: Context) ac let private skipAttribute (name : string) = // TODO: skip all attributes where definiton not known??? - name.StartsWith "Microsoft.FSharp.Core" || - name.StartsWith "System.Reflection" || - name.StartsWith "System.Runtime.CompilerServices" || - name.StartsWith "System.ObsoleteAttribute" || - name.StartsWith "System.Diagnostics" + // name.StartsWith "Microsoft.FSharp.Core" || + // name.StartsWith "System.Reflection" || + // name.StartsWith "System.Runtime.CompilerServices" || + // name.StartsWith "System.ObsoleteAttribute" || + // name.StartsWith "System.Diagnostics" + false let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharpAttribute) = match a.AttributeType.TryFullName with @@ -310,11 +311,29 @@ let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharp a.ConstructorArguments |> Seq.toList |> List.map (fun (t,v) -> match v with | :? string as str -> Fable.Value(Fable.StringConstant str, None) + | :? int8 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.Int8), None) + | :? uint8 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.UInt8), None) + | :? int16 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.Int16), None) + | :? uint16 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.UInt16), None) + | :? int32 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.Int32), None) + | :? uint32 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.UInt32), None) + | :? float32 as v -> Fable.Value(Fable.NumberConstant(float v, NumberKind.Float32), None) + | :? float as v -> Fable.Value(Fable.NumberConstant(v, NumberKind.Float64), None) | _ -> Fable.Value(Fable.StringConstant (string v), None) ) + + let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType - let x = makeCallFrom com ctx None typ false [] None args ctor - Some (fullname,x) + try + let x = makeCallFrom com ctx None typ false [] None args ctor + match x with + | AST.Fable.Value(AST.Fable.Null AST.Fable.Any, None) -> + com.RemoveLastError() + None + | _ -> + Some (fullname,x) + with _ -> + None | _ -> None | _ -> @@ -1545,6 +1564,9 @@ type FableCompiler(com: ICompiler, implFiles: IDictionaryInlineExpr) -> InlineExpr abstract AddLog: msg:string * severity: Severity * ?range:SourceLocation * ?fileName:string * ?tag: string -> unit + abstract RemoveLastError : unit -> unit diff --git a/src/Fable.Transforms/State.fs b/src/Fable.Transforms/State.fs index fded469a62..8fa30ee408 100644 --- a/src/Fable.Transforms/State.fs +++ b/src/Fable.Transforms/State.fs @@ -94,6 +94,13 @@ type Compiler(currentFile, project: Project, options, fableLibraryDir: string) = Range = range FileName = fileName } |> logs.Add + + member __.RemoveLastError() = + let mutable i = logs.Count - 1 + while i >= 0 && logs.[i].Severity <> Severity.Error do + i <- i - 1 + if i >= 0 then logs.RemoveAt i + // TODO: If name includes `$$2` at the end, remove it member __.GetUniqueVar(name) = id <- id + 1 diff --git a/tests/Main/ReflectionTests.fs b/tests/Main/ReflectionTests.fs index 9a9454462e..dc1913b054 100644 --- a/tests/Main/ReflectionTests.fs +++ b/tests/Main/ReflectionTests.fs @@ -23,6 +23,7 @@ type TestType = type TestType2 = | Union2 of string +[] type TestType3 = class end type TestType4 = class end type TestType5 = class end From 38a24948794d1e6531dd6e1b99af1beb07248d4b Mon Sep 17 00:00:00 2001 From: ncave <777696+ncave@users.noreply.github.com> Date: Thu, 4 Jun 2020 10:21:54 -0700 Subject: [PATCH 38/38] Resoved conflicts --- src/Fable.Transforms/FSharp2Fable.Util.fs | 11 +- src/Fable.Transforms/FSharp2Fable.fs | 13 +- src/Fable.Transforms/Fable2Babel.fs | 144 ++-------------------- src/fable-standalone/src/Main.fs | 1 + tests/Main/ReflectionTests.fs | 10 +- 5 files changed, 27 insertions(+), 152 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 0dece6d101..118b7a25b2 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -105,16 +105,16 @@ module Helpers = ||> Naming.sanitizeIdent (fun _ -> false) let getModuleReflectionName (com: ICompiler) (ent : FSharpEntity) = - if ent.IsFSharpModule then - let name = + if ent.IsFSharpModule then + let name = (getEntityMangledName com true ent, Naming.NoMemberPart) ||> Naming.sanitizeIdent (fun _ -> false) - if name = "" then + if name = "" then Some "" - else + else Some name else - None + None let isUnit (typ: FSharpType) = let typ = nonAbbreviatedType typ @@ -156,6 +156,7 @@ module Helpers = | Naming.StaticMemberPart(_, overloadSuffix) -> String.IsNullOrEmpty(overloadSuffix) |> not | Naming.NoMemberPart -> false + | Naming.ReflectionMemberPart -> false sanitizedName, hasOverloadSuffix /// Used to identify members uniquely in the inline expressions dictionary diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index d52e5014d0..eb3cc90bf8 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -331,8 +331,8 @@ let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharp | :? float as v -> Fable.Value(Fable.NumberConstant(v, NumberKind.Float64), None) | _ -> Fable.Value(Fable.StringConstant (string v), None) ) - - + + let typ = makeTypeFromDef com ctx.GenericArgs (System.Collections.Generic.List() :> IList<_>) a.AttributeType try let x = makeCallFrom com ctx None typ false [] None args ctor @@ -340,10 +340,10 @@ let private transformAttribute (com: IFableCompiler) (ctx : Context) (a : FSharp | AST.Fable.Value(AST.Fable.Null AST.Fable.Any, None) -> com.RemoveLastError() None - | _ -> + | _ -> Some (fullname,x) with _ -> - None + None | _ -> None | _ -> @@ -485,7 +485,8 @@ let private transformMemberInfo (com: IFableCompiler) ctx (m : FSharpMemberOrFun Fable.Args = args Fable.SignatureArgTypes = Fable.SignatureKind.NoUncurrying Fable.Spread = Fable.SpreadKind.NoSpread - Fable.IsBaseOrSelfConstructorCall = false + Fable.IsBaseCall = false + Fable.IsSelfConstructorCall = false } staticCall None decl info (makeValueFrom com ctx None m) @@ -1613,7 +1614,7 @@ type FableCompiler(com: ICompiler, implFiles: IDictionary upcast AnyTypeAnnotation() | Fable.MetaType -> upcast AnyTypeAnnotation() | Fable.Any -> upcast AnyTypeAnnotation() | Fable.Unit -> upcast VoidTypeAnnotation() @@ -608,7 +609,6 @@ module Util = | Fable.AsPojo(expr, caseRule) -> com.TransformAsExpr(ctx, Replacements.makePojo com r caseRule expr) | Fable.Curry(expr, arity) -> com.TransformAsExpr(ctx, Replacements.curryExprAtRuntime arity expr) -<<<<<<< HEAD let rec transformMemberReflectionInfosNew (com : IBabelCompiler) ctx r (self : Expression) (generics : Expression) (ent: FSharpEntity) (mems : Fable.MemberInfo[]) = let genMap = ent.GenericParameters |> Seq.mapi (fun i x -> x.Name, i) |> Map.ofSeq @@ -637,25 +637,6 @@ module Util = let meth = coreValue com ctx "Reflection" "NMethodInfo" let parameters = parameters |> Array.map newParameter let ret = transformTypeInfo com ctx r [||] genMap ret -======= - let rec transformRecordReflectionInfo com ctx r (ent: FSharpEntity) generics = - // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo - let fullname = defaultArg ent.TryFullName Naming.unknown - let fullnameExpr = StringLiteral fullname :> Expression - let genMap = - let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - Array.zip genParamNames generics |> Map - let fields = - ent.FSharpFields |> Seq.map (fun fi -> - let typeInfo = - FSharp2Fable.TypeHelpers.makeType com Map.empty fi.FieldType - |> transformTypeInfo com ctx r genMap - (ArrayExpression [|StringLiteral fi.Name; typeInfo|] :> Expression)) - |> Seq.toArray - let fields = ArrowFunctionExpression([||], ArrayExpression fields :> Expression |> U2.Case2) :> Expression - [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; fields|] - |> coreReflectionCall com ctx None "record" ->>>>>>> master let genPars = genericParameters |> Array.map (fun n -> coreLibCall com ctx None "Reflection" "getGenericParameter" [| StringLiteral n |]) |> ArrayExpression :> Expression @@ -738,7 +719,6 @@ module Util = // TODO: Refactor these three bindings to reuse in transformUnionReflectionInfo let fullname = defaultArg ent.TryFullName Naming.unknown let fullnameExpr = StringLiteral fullname :> Expression -<<<<<<< HEAD let genParamNames = ent.GenericParameters |> Seq.map (fun x -> StringLiteral x.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression //let genMap = Array.zip genParamNames generics |> Map @@ -796,39 +776,6 @@ module Util = // |> coreLibCall com ctx None "Reflection" "union" and transformTypeInfo (com: IBabelCompiler) ctx r (mems : Fable.MemberInfo[]) (genMap: string -> Option) t: Expression = -======= - let genMap = - let genParamNames = ent.GenericParameters |> Seq.map (fun x -> x.Name) |> Seq.toArray - Array.zip genParamNames generics |> Map - let cases = - ent.UnionCases |> Seq.map (fun uci -> - let fieldInfos = - uci.UnionCaseFields - |> Seq.map (fun fi -> - let fieldType = - FSharp2Fable.TypeHelpers.makeType com Map.empty fi.FieldType - |> transformTypeInfo com ctx r genMap - ArrayExpression [| - fi.Name |> StringLiteral :> Expression - fieldType - |] :> Expression - ) - |> Seq.toArray - let caseInfo = - if fieldInfos.Length = 0 then - getUnionCaseName uci |> StringLiteral :> Expression - else - ArrayExpression [| - getUnionCaseName uci |> StringLiteral :> Expression - ArrayExpression fieldInfos :> Expression - |] :> Expression - caseInfo) |> Seq.toArray - let cases = ArrowFunctionExpression([||], ArrayExpression cases :> Expression |> U2.Case2) :> Expression - [|fullnameExpr; upcast ArrayExpression generics; jsConstructor com ctx ent; cases|] - |> coreReflectionCall com ctx None "union" - - and transformTypeInfo (com: IBabelCompiler) ctx r (genMap: Map) t: Expression = ->>>>>>> master let primitiveTypeInfo name = coreValue com ctx "Reflection" (name + "_type") let numberInfo kind = @@ -836,15 +783,10 @@ module Util = |> primitiveTypeInfo let nonGenericTypeInfo fullname = [| StringLiteral fullname :> Expression |] -<<<<<<< HEAD |> coreLibCall com ctx None "Reflection" "ntype" -======= - |> coreReflectionCall com ctx None "class" ->>>>>>> master let resolveGenerics generics: Expression[] = generics |> Array.map (transformTypeInfo com ctx r [||] genMap) let genericTypeInfo name genArgs = -<<<<<<< HEAD let gen = genArgs |> Array.map (function Fable.GenericParam n -> coreLibCall com ctx None "Reflection" "getGenericParameter" [|StringLiteral n|] | t -> transformTypeInfo com ctx r [||] genMap t) coreLibCall com ctx None "Reflection" name gen let genericEntity (ent: FSharpEntity) generics = @@ -853,15 +795,6 @@ module Util = let genericNames = ent.GenericParameters |> Seq.map (fun p -> StringLiteral p.Name :> Expression) |> Seq.toArray |> ArrayExpression :> Expression let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; genericNames; ArrayExpression generics :> Expression|] coreLibCall com ctx None "Reflection" "ntype" args -======= - let resolved = resolveGenerics genArgs - coreReflectionCall com ctx None name resolved - let genericEntity (ent: FSharpEntity) generics = - let fullname = defaultArg ent.TryFullName Naming.unknown - let fullnameExpr = StringLiteral fullname :> Expression - let args = if Array.isEmpty generics then [|fullnameExpr|] else [|fullnameExpr; ArrayExpression generics :> Expression|] - coreReflectionCall com ctx None "class" args ->>>>>>> master match t with | Fable.ErasedUnion _genArgs -> primitiveTypeInfo "obj" // TODO: Type info for ErasedUnion? | Fable.Any -> primitiveTypeInfo "obj" @@ -908,13 +841,9 @@ module Util = let genArgs = resolveGenerics (List.toArray genArgs) Array.zip fieldNames genArgs |> Array.map (fun (k, t) -> ArrayExpression [|StringLiteral k; t|] :> Expression) -<<<<<<< HEAD |> coreLibCall com ctx None "Reflection" "anonRecord" | Fable.Expr None -> nonGenericTypeInfo "Expr" | Fable.Expr (Some gen) -> genericTypeInfo "Expr" [|gen|] -======= - |> coreReflectionCall com ctx None "anonRecord" ->>>>>>> master | Fable.DeclaredType(ent, generics) -> match ent, generics with | Replacements.BuiltinEntity kind -> @@ -989,7 +918,6 @@ module Util = let callee = com.TransformAsExpr(ctx, reflectionMethodExpr) CallExpression(callee, generics) :> Expression -<<<<<<< HEAD let transformReflectionInfo com ctx r (ent: FSharpEntity) declaringName (mems : Fable.MemberInfo[]) generics = transformRecordReflectionInfo com ctx r ent declaringName mems generics // if ent.IsFSharpRecord then @@ -1010,30 +938,6 @@ module Util = match value with //| Fable.TypeDefInf | Fable.TypeInfo t -> transformTypeInfo com ctx r [||] (fun _ -> None) t - | Fable.Null _ -> upcast NullLiteral(?loc=r) - | Fable.UnitConstant -> upcast NullLiteral(?loc=r) // TODO: Use `void 0`? -======= - let transformReflectionInfo com ctx r (ent: FSharpEntity) generics = - if ent.IsFSharpRecord then - transformRecordReflectionInfo com ctx r ent generics - elif ent.IsFSharpUnion then - transformUnionReflectionInfo com ctx r ent generics - else - let fullname = defaultArg ent.TryFullName Naming.unknown - [| - yield StringLiteral fullname :> Expression - match generics with - | [||] -> yield Undefined() :> Expression - | generics -> yield ArrayExpression generics :> _ - match tryJsConstructor com ctx ent with - | Some cons -> yield cons - | None -> () - |] - |> coreReflectionCall com ctx None "class" - - let transformValue (com: IBabelCompiler) (ctx: Context) r value: Expression = - match value with - | Fable.TypeInfo t -> transformTypeInfo com ctx r Map.empty t | Fable.Null _t -> // if com.Options.typescript // let ta = typeAnnotation com ctx t |> TypeAnnotation |> Some @@ -1041,7 +945,6 @@ module Util = // else upcast NullLiteral(?loc=r) | Fable.UnitConstant -> upcast UnaryExpression(UnaryVoid, NullLiteral(), ?loc=r) ->>>>>>> master | Fable.BoolConstant x -> upcast BooleanLiteral(x, ?loc=r) | Fable.CharConstant x -> upcast StringLiteral(string x, ?loc=r) | Fable.StringConstant x -> upcast StringLiteral(x, ?loc=r) @@ -1397,7 +1300,6 @@ module Util = | Fable.FunctionType _ -> jsTypeof "function" expr | Fable.Array _ | Fable.Tuple _ -> coreLibCall com ctx None "Util" "isArrayLike" [|com.TransformAsExpr(ctx, expr)|] -<<<<<<< HEAD | Fable.List _ -> jsInstanceof (coreValue com ctx "Types" "List") expr @@ -1405,9 +1307,6 @@ module Util = jsInstanceof (coreValue com ctx "Quotations" "FSharpExpr") expr //coreLibCall com ctx None "ExprUtils" "isExpr" [| com.TransformAsExpr(ctx, expr) |] -======= - | Fable.List _ -> jsInstanceof (coreValue com ctx "Types" "List") expr ->>>>>>> master | Replacements.Builtin kind -> match kind with | Replacements.BclGuid -> jsTypeof "string" expr @@ -2074,9 +1973,6 @@ module Util = | _ -> None ) -<<<<<<< HEAD - let declareType com ctx r isPublic declaringName (mems : Fable.MemberInfo[]) (ent: FSharpEntity) name consArgs consBody baseExpr: U2 list = -======= let getGenericTypeAnnotation com ctx name genParams = let id = Identifier(name) let typeParamInst = makeTypeParamInst genParams @@ -2156,7 +2052,6 @@ module Util = InterfaceDeclaration(id, body, ?extends_=extends, ?typeParameters=typeParamDecl, ?loc=r) let declareObjectType (com: IBabelCompiler) ctx r isPublic (ent: FSharpEntity) name (consArgs: Pattern[]) (consBody: BlockStatement) (baseExpr: Expression option) = ->>>>>>> master let displayName = ent.TryGetFullDisplayName() |> Option.map (Naming.unsafeReplaceIdentForbiddenChars '_') @@ -2206,19 +2101,14 @@ module Util = let classExpr = ClassExpression(classBody, ?superClass=Some baseRef, ?typeParameters=typeParamDecl, ?loc=r) classExpr |> declareModuleMember r isPublic name false - let declareType (com: IBabelCompiler) ctx r isPublic (ent: FSharpEntity) name (consArgs: Pattern[]) (consBody: BlockStatement) baseExpr: U2 list = + let declareType (com: IBabelCompiler) ctx r isPublic declaringName (mems: Fable.MemberInfo[]) (ent: FSharpEntity) name (consArgs: Pattern[]) (consBody: BlockStatement) baseExpr: U2 list = let typeDeclaration = if com.Options.classTypes then declareClassType com ctx r isPublic ent name consArgs consBody baseExpr else declareObjectType com ctx r isPublic ent name consArgs consBody baseExpr let reflectionDeclaration = -<<<<<<< HEAD - let genArgs = Array.init ent.GenericParameters.Count (fun _ -> makeIdentUnique com "gen" |> ident) - let body = transformReflectionInfo com ctx r ent declaringName mems (Array.map (fun x -> x :> _) genArgs) - makeFunctionExpression None (Array.map (fun x -> U2.Case2(upcast x)) genArgs) (U2.Case2 body) -======= let genArgs = Array.init ent.GenericParameters.Count (fun _ -> makeIdentUnique com "gen" |> typedIdent com ctx) - let body = transformReflectionInfo com ctx r ent (Array.map (fun x -> x :> _) genArgs) + let body = transformReflectionInfo com ctx r ent declaringName mems (Array.map (fun x -> x :> _) genArgs) let returnType = if com.Options.typescript then makeImportTypeAnnotation com ctx [] "Reflection" "TypeInfo" @@ -2226,7 +2116,6 @@ module Util = else None let args = genArgs |> Array.map toPattern makeFunctionExpression None (args, U2.Case2 body, returnType, None) ->>>>>>> master |> declareModuleMember None isPublic (Naming.appendSuffix name Naming.reflectionSuffix) false if com.Options.typescript then // && not (com.Options.classTypes && (ent.IsFSharpUnion || ent.IsFSharpRecord)) then let interfaceDecl = makeInterfaceDecl com ctx r ent name baseExpr @@ -2299,7 +2188,7 @@ module Util = |> ExpressionStatement :> Statement |> U2<_,ModuleDeclaration>.Case1 |> List.singleton - let transformUnionConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.UnionConstructorInfo) = + let transformUnionConstructor (com: IBabelCompiler) ctx r (declaringName: Option) (info: Fable.UnionConstructorInfo) = let baseRef = coreValue com ctx "Types" "Union" let argId: Fable.Ident = { Name = ""; Type = Fable.Any; Kind = Fable.UserDeclared; IsMutable = false; Range = None } let tagId = { argId with Name = "tag"; Type = Fable.Number Int32 } @@ -2310,17 +2199,6 @@ module Util = typedIdent com ctx nameId |> toPattern typedIdent com ctx fieldsId |> restElement |] let body = -<<<<<<< HEAD - [Identifier "tag" :> Expression; Identifier "name" :> _; SpreadElement(Identifier "fields") :> _] - |> callFunctionWithThisContext None baseRef thisExpr |> ExpressionStatement - declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args (BlockStatement [|body|]) (Some baseRef) - - let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.CompilerGeneratedConstructorInfo) = - let args = - [| for i = 1 to info.Entity.FSharpFields.Count do - yield Identifier("arg" + string i) |] - let setters = -======= if com.Options.classTypes then [ (ident tagId) :> Expression (ident nameId) :> Expression @@ -2338,13 +2216,12 @@ module Util = | _ -> ident id :> Expression assign None left right |> ExpressionStatement :> Statement) |> BlockStatement - declareType com ctx r info.IsPublic info.Entity info.EntityName args body (Some baseRef) + declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args body (Some baseRef) - let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (info: Fable.CompilerGeneratedConstructorInfo) = + let transformCompilerGeneratedConstructor (com: IBabelCompiler) ctx r (declaringName: Option) (info: Fable.CompilerGeneratedConstructorInfo) = let fieldIds = getEntityFieldsAsIdents com info.Entity let args = fieldIds |> Array.map ident let body = ->>>>>>> master info.Entity.FSharpFields |> Seq.mapi (fun i field -> let left = get None thisExpr field.Name @@ -2361,14 +2238,9 @@ module Util = elif info.Entity.IsFSharpRecord || info.Entity.IsValueType then coreValue com ctx "Types" "Record" |> Some else None -<<<<<<< HEAD - let args = [|for arg in args do yield arg |> toPattern|] - declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args (BlockStatement setters) baseExpr -======= let typedPattern = typedIdent com ctx >> toPattern let args = fieldIds |> Array.map typedPattern - declareType com ctx r info.IsPublic info.Entity info.EntityName args body baseExpr ->>>>>>> master + declareType com ctx r info.IsPublic declaringName info.Members info.Entity info.EntityName args body baseExpr let transformImplicitConstructor (com: IBabelCompiler) ctx r (declaringName : Option) (info: Fable.ClassImplicitConstructorInfo) = let boundThis = Some("this", info.BoundConstructorThis) @@ -2489,7 +2361,7 @@ module Util = //transformed |> transformDeclarations com ctx restDecls let reflectionDeclaration = let body = transformRecordReflectionInfo com ctx None ent declaringName mems [||] - makeFunctionExpression None [||] (U2.Case2 body) + makeFunctionExpression None ([||], U2.Case2 body, None, None) |> declareModuleMember None true (Naming.appendSuffix name Naming.reflectionSuffix) false List.append transformed [reflectionDeclaration] |> transformDeclarations com ctx restDecls diff --git a/src/fable-standalone/src/Main.fs b/src/fable-standalone/src/Main.fs index a44f873ca8..99688d1694 100644 --- a/src/fable-standalone/src/Main.fs +++ b/src/fable-standalone/src/Main.fs @@ -230,6 +230,7 @@ let makeCompilerOptions (config: CompilerConfig option) (otherFSharpOptions: str debugMode = isDebug verbosity = Fable.Verbosity.Normal outputPublicInlinedFunctions = false + quotations = false precompiledLib = config.precompiledLib } let compileAst (com: Compiler) (project: Project) = diff --git a/tests/Main/ReflectionTests.fs b/tests/Main/ReflectionTests.fs index 1c324316f1..4c84ce06e5 100644 --- a/tests/Main/ReflectionTests.fs +++ b/tests/Main/ReflectionTests.fs @@ -517,13 +517,13 @@ type MyRecord20 = { FieldA: int FieldB: string } -type MyClass<'a>(value : 'a) = +type MyClass3<'a>(value : 'a) = static member GetValue<'b>(a : 'a, b : 'b) = (a,b) member x.Value : 'a = value let fableTests = [ testCase "Generic static method can be instantiated and invoked" <| fun () -> - let tm = typedefof>.MakeGenericType [| typeof |] + let tm = typedefof>.MakeGenericType [| typeof |] let meth = tm.GetMethod("GetValue") let m = meth.MakeGenericMethod [| typeof |] let pars = m.GetParameters() |> Array.map (fun p -> p.ParameterType, p.Name) @@ -535,10 +535,10 @@ let fableTests = [ testCase "Property can be read" <| fun () -> - let tm = typedefof>.MakeGenericType [| typeof |] + let tm = typedefof>.MakeGenericType [| typeof |] let prop = tm.GetProperty("Value") - let v1 = prop.GetMethod.Invoke(MyClass(1), null) |> unbox - let v2 = prop.GetValue(MyClass(1)) |> unbox + let v1 = prop.GetMethod.Invoke(MyClass3(1), null) |> unbox + let v2 = prop.GetValue(MyClass3(1)) |> unbox v1 |> equal 1 v2 |> equal 1