diff --git a/src/compiler/WebSharper.Compiler/Breaker.fs b/src/compiler/WebSharper.Compiler/Breaker.fs index d7969c204..f0e122b53 100644 --- a/src/compiler/WebSharper.Compiler/Breaker.fs +++ b/src/compiler/WebSharper.Compiler/Breaker.fs @@ -39,7 +39,7 @@ type Broken<'a> = { Body : 'a Statements : Statement list - Variables : Id list + Variables : (Id * Statement option) list } let broken b = @@ -64,7 +64,7 @@ let bindBroken f b = } let private getExpr b = match b with ResultVar v -> Var v | ResultExpr e -> e -let private getVarList b = match b with ResultVar v -> [ v ] | ResultExpr e -> [] +let private getVarList b = match b with ResultVar v -> [ v, None ] | ResultExpr e -> [] let toBrExpr b = { @@ -117,10 +117,22 @@ type MarkApplicationsPure(v) = ReplaceId(id, v).TransformExpression(body) |> this.TransformExpression | _ -> base.TransformLet(id, value, body) +let toDecls (vars: _ list) = + seq { + for vv in vars do + match vv with + | v, None -> yield VarDeclaration(v, Undefined) + | _ -> () + for vv in vars do + match vv with + | _, Some s -> yield s + | _ -> () + } + let toStatements f b = let b = toBrExpr b seq { - for v in b.Variables -> VarDeclaration(v, Undefined) + yield! toDecls b.Variables yield! b.Statements yield f b.Body } @@ -128,7 +140,7 @@ let toStatements f b = let toStatementsL f (b: Broken)= let b = toBrExpr b seq { - for v in b.Variables -> VarDeclaration(v, Undefined) + yield! toDecls b.Variables yield! b.Statements yield! f b.Body } @@ -142,13 +154,13 @@ let toStatementExpr b = match b.Body with | ResultVar v -> seq { - for v in b.Variables -> VarDeclaration(v, Undefined) + yield! toDecls b.Variables for st in b.Statements -> TransformVarSets(v, id).TransformStatement(st) } | ResultExpr e -> seq { - for v in b.Variables -> VarDeclaration(v, Undefined) + yield! toDecls b.Variables yield! b.Statements if not (isPureExpr e) then yield ExprStatement (ignoreVoid e) @@ -205,7 +217,7 @@ let rec breakExpr expr : Broken = bL true (b.Variables @ accVar, accSt, b.Body :: accE) bRest else let v = Id.New() - bL true (v :: b.Variables @ accVar, VarSetStatement (v, getExpr b.Body) :: accSt, ResultVar v :: accE) bRest + bL true ((v, None) :: b.Variables @ accVar, VarSetStatement (v, getExpr b.Body) :: accSt, ResultVar v :: accE) bRest else bL false (b.Variables @ accVar, accSt, b.Body :: accE) bRest else @@ -213,7 +225,7 @@ let rec breakExpr expr : Broken = bL true (b.Variables @ accVar, b.Statements @ accSt, b.Body :: accE) bRest else let v = Id.New() - bL true (v :: b.Variables @ accVar, b.Statements @ VarSetStatement (v, getExpr b.Body) :: accSt, ResultVar v :: accE) bRest + bL true ((v, None) :: b.Variables @ accVar, b.Statements @ VarSetStatement (v, getExpr b.Body) :: accSt, ResultVar v :: accE) bRest let vars, st, e = bL false ([], [], []) (List.rev bb) { Body = e @@ -308,7 +320,7 @@ let rec breakExpr expr : Broken = else brA.Statements |> List.map (TransformMoreVarSets(removeVars, id).TransformStatement) @ (extraExprs |> List.map ExprStatement) - Variables = brA.Variables |> List.filter (fun v -> removeVars |> List.contains v |> not) + Variables = brA.Variables |> List.filter (fun (v, _) -> removeVars |> List.contains v |> not) } | NewArray a -> brL a |> mapBroken NewArray @@ -395,13 +407,13 @@ let rec breakExpr expr : Broken = { Body = ResultVar a Statements = brB.Statements |> List.map (TransformVarSets(v, fun e -> VarSet(a, e)).TransformStatement) - Variables = [ a ] + Variables = brB.Variables } | ResultExpr e -> { Body = ResultExpr (Void (VarSet(a, e))) Statements = brB.Statements - Variables = [ a ] + Variables = [ a, None ] @ brB.Variables } | StatementExpr (I.ExprStatement a, None) -> br a @@ -410,7 +422,7 @@ let rec breakExpr expr : Broken = { Body = ResultExpr (Sequential [brA.Body; Var b]) Statements = brA.Statements - Variables = b :: brA.Variables + Variables = (b, None) :: brA.Variables } | StatementExpr (st, v) -> { @@ -450,7 +462,9 @@ let rec breakExpr expr : Broken = | Let (var, value, I.Function ([x], (I.ExprStatement(I.Application (I.Var f, [I.Var y], _, _)) | I.Return(I.Application (I.Var f, [I.Var y], _, _))))) when f = var && x = y -> br value - | Let(a, IgnoreSourcePos.Var b, c) + | Let(a, b, I.Var c) when a = c -> + br b + | Let(a, I.Var b, c) when (not b.IsMutable) || (notMutatedOrCaptured a c && notMutatedOrCaptured b c) -> // TODO: maybe weaker check is enough ReplaceId(a, b).TransformExpression(c) |> br | Let(var, value, I.Application(func, [I.Var v], p, l)) @@ -458,6 +472,14 @@ let rec breakExpr expr : Broken = Application(func, [value], p, l) |> br | Let (objVar, I.Object objFields, I.Sequential (PropSetters (setters, v))) when v = objVar -> objFields @ setters |> Object |> br + | Let(var, I.Function(args, body), c) + when notMutatedOrCaptured var c && CountVarOccurence(var).Get(c) >= 2 -> + let brC = br c + { + Body = brC.Body + Statements = brC.Statements + Variables = (var, Some (FuncDeclaration(var, args, BreakStatement body))) :: brC.Variables + } | Let(a, b, c) -> let brB = br b match brB.Body with @@ -489,7 +511,7 @@ let rec breakExpr expr : Broken = { Body = ResultExpr(Sequential [VarSet (a, brB.Body); brC.Body ]) Statements = [] - Variables = a :: brB.Variables @ brC.Variables + Variables = (a, None) :: brB.Variables @ brC.Variables } else { @@ -504,7 +526,7 @@ let rec breakExpr expr : Broken = { Body = ResultExpr(Sequential [VarSet (a, brB.Body); brC.Body ]) Statements = brB.Statements - Variables = a :: brB.Variables @ brC.Variables + Variables = (a, None) :: brB.Variables @ brC.Variables } else { @@ -519,13 +541,13 @@ let rec breakExpr expr : Broken = Statements = (brB.Statements |> List.map (TransformVarSets(bv, fun e -> VarSet(a, e)).TransformStatement)) @ brC.Statements - Variables = a :: brB.Variables @ brC.Variables + Variables = (a, None) :: brB.Variables @ brC.Variables } | NewVar(a, Undefined) -> { Body = ResultExpr(Undefined) Statements = [] - Variables = [ a ] + Variables = [ a, None ] } | NewVar(a, b) -> let brB = br b @@ -534,7 +556,7 @@ let rec breakExpr expr : Broken = { Body = ResultExpr(VarSet (a, e)) Statements = brB.Statements - Variables = a :: brB.Variables + Variables = (a, None) :: brB.Variables } | ResultVar v -> { @@ -569,14 +591,12 @@ let rec breakExpr expr : Broken = Body = ResultExpr brB.Body Statements = [ - for i, _ in a do - yield VarDeclaration(i, Undefined) for i, v in brAs do yield! v.Statements yield VarSetStatement(i, v.Body) yield! brB.Statements ] - Variables = brB.Variables + Variables = (brAs |> List.collect (fun (v, ba) -> (v, None) :: ba.Variables)) @ brB.Variables } | New(a, b) -> brL (a :: b) @@ -614,6 +634,8 @@ and private breakSt statement : Statement seq = Seq.singleton (Labeled (a, combine (brS b))) | VarDeclaration (a, b) -> brE b |> toStatements (fun bE -> VarDeclaration (a, bE)) + | FuncDeclaration (a, b, c) -> + Seq.singleton (FuncDeclaration (a, b, combine (brS c))) | While (a, b) -> let brA = brE a if hasNoStatements brA then @@ -644,7 +666,7 @@ and private breakSt statement : Statement seq = else let brB = brB |> toBrExpr [ - for v in brB.Variables -> VarDeclaration(v, Undefined) + yield! toDecls brB.Variables yield DoWhile (combine (Seq.append (brS a) brB.Statements), brB.Body) ] |> Seq.ofList @@ -657,16 +679,19 @@ and private breakSt statement : Statement seq = [ match brA with | Some brA -> - yield! brA.Statements - for v in brA.Variables -> VarDeclaration(v, Undefined) + yield! toDecls brA.Variables | _ -> () match brB with | Some brB -> - for v in brB.Variables -> VarDeclaration(v, Undefined) + yield! toDecls brB.Variables | _ -> () match brC with | Some brC -> - for v in brC.Variables -> VarDeclaration(v, Undefined) + yield! toDecls brC.Variables + | _ -> () + match brA with + | Some brA -> + yield! brA.Statements | _ -> () yield For(get brA, get brB, get brC, combine (brS d)) ] diff --git a/src/compiler/WebSharper.Compiler/CompilationHelpers.fs b/src/compiler/WebSharper.Compiler/CompilationHelpers.fs index 2cae062dc..69e256863 100644 --- a/src/compiler/WebSharper.Compiler/CompilationHelpers.fs +++ b/src/compiler/WebSharper.Compiler/CompilationHelpers.fs @@ -447,7 +447,7 @@ module JSRuntime = let private runtime = ["Runtime"; "IntelliFactory"] let private runtimeFunc f p args = Application(GlobalAccess (Address (f :: runtime)), args, p, Some (List.length args)) let private runtimeFuncI f p i args = Application(GlobalAccess (Address (f :: runtime)), args, p, Some i) - let Class members basePrototype statics = runtimeFunc "Class" true [members; basePrototype; statics] + let Class members basePrototype statics name = runtimeFunc "Class" true [members; basePrototype; statics; Value (String name)] let Ctor ctor typeFunction = runtimeFunc "Ctor" true [ctor; typeFunction] let Cctor cctor = runtimeFunc "Cctor" true [cctor] let GetOptional value = runtimeFunc "GetOptional" true [value] diff --git a/src/compiler/WebSharper.Compiler/JavaScriptWriter.fs b/src/compiler/WebSharper.Compiler/JavaScriptWriter.fs index 752e33969..5d47a5144 100644 --- a/src/compiler/WebSharper.Compiler/JavaScriptWriter.fs +++ b/src/compiler/WebSharper.Compiler/JavaScriptWriter.fs @@ -37,6 +37,7 @@ type Environment = mutable ScopeVars : Set mutable CompactVars : int mutable ScopeIds : Map + ScopeFuncs : ResizeArray } static member New(name, pref) = { @@ -44,16 +45,18 @@ type Environment = Preference = pref ScopeVars = Set.empty CompactVars = 0 - ScopeIds = Map.empty + ScopeIds = Map.empty + ScopeFuncs = ResizeArray() } - member this.Clone() = + member this.NewInner() = { AssemblyName = this.AssemblyName Preference = this.Preference ScopeVars = this.ScopeVars CompactVars = this.CompactVars ScopeIds = this.ScopeIds + ScopeFuncs = ResizeArray() } let undef = J.Unary(J.UnaryOperator.``void``, J.Constant (J.Literal.Number "0")) @@ -89,7 +92,6 @@ let rec transformExpr (env: Environment) (expr: Expression) : J.Expression = match expr with | Undefined -> undef | This -> J.This -// | Global -> glob | Var id -> J.Var (trI id) | Value v -> match v with @@ -123,7 +125,7 @@ let rec transformExpr (env: Environment) (expr: Expression) : J.Expression = } : J.SourcePos J.ExprPos (trE e, jpos) | Function (ids, b) -> - let innerEnv = env.Clone() + let innerEnv = env.NewInner() let args = ids |> List.map (defineId innerEnv) let body = match b |> transformStatement innerEnv with @@ -132,9 +134,10 @@ let rec transformExpr (env: Environment) (expr: Expression) : J.Expression = | J.Return None :: more -> List.rev more | _ -> b |> List.map J.Action + | J.Empty | J.Return None -> [] | b -> [ b |> J.Action ] - J.Lambda(None, args, body) + J.Lambda(None, args, List.ofSeq innerEnv.ScopeFuncs @ body) | ItemGet (x, y) | ItemGetNonPure (x, y) -> (trE x).[trE y] @@ -250,6 +253,8 @@ and transformStatement (env: Environment) (statement: Statement) : J.Statement = emptyDecls.Add (defineId env id) | _ -> decls.Add (defineId env id, trE e) + | FuncDeclaration _ -> + trS a |> ignore | Empty | ExprStatement IgnoreSourcePos.Undefined -> () | ExprStatement (IgnoreSourcePos.Sequential s) -> @@ -295,6 +300,22 @@ and transformStatement (env: Environment) (statement: Statement) : J.Statement = | Return a -> J.Return (Some (trE a)) | VarDeclaration (id, e) -> J.Vars ([defineId env id, match e with IgnoreSourcePos.Undefined -> None | _ -> Some (trE e)]) + | FuncDeclaration (x, ids, b) -> + let id = defineId env x + let innerEnv = env.NewInner() + let args = ids |> List.map (defineId innerEnv) + let body = + match b |> transformStatement innerEnv with + | J.Block b -> + match List.rev b with + | J.Return None :: more -> List.rev more + | _ -> b + |> List.map J.Action + | J.Empty + | J.Return None -> [] + | b -> [ b |> J.Action ] + J.Function(id, args, List.ofSeq innerEnv.ScopeFuncs @ body) |> env.ScopeFuncs.Add + J.Empty | While(a, b) -> J.While (trE a, trS b) | DoWhile(a, b) -> J.Do (trS a, trE b) | For(a, b, c, d) -> J.For(Option.map trE a, Option.map trE b, Option.map trE c, trS d) diff --git a/src/compiler/WebSharper.Compiler/Packager.fs b/src/compiler/WebSharper.Compiler/Packager.fs index 7038dfcb8..9253b09a2 100644 --- a/src/compiler/WebSharper.Compiler/Packager.fs +++ b/src/compiler/WebSharper.Compiler/Packager.fs @@ -116,14 +116,14 @@ let packageAssembly (refMeta: M.Info) (current: M.Info) isBundle = | M.Macro (_, _, Some fb) -> withoutMacros fb | _ -> info - let rec packageClass (c: M.ClassInfo) = + let rec packageClass (c: M.ClassInfo) name = match c.BaseClass with | Some b -> match classes.TryFind b with | Some bc -> classes.Remove b |> ignore - packageClass bc + packageClass bc b.Value.FullName | _ -> () | _ -> () @@ -165,7 +165,7 @@ let packageAssembly (refMeta: M.Info) (current: M.Info) isBundle = | _ -> Value Null if c.HasWSPrototype then - packageCtor addr <| JSRuntime.Class prototype baseType (GlobalAccess addr) + packageCtor addr <| JSRuntime.Class prototype baseType (GlobalAccess addr) name for info, _, body in c.Methods.Values do match withoutMacros info with @@ -194,7 +194,7 @@ let packageAssembly (refMeta: M.Info) (current: M.Info) isBundle = while classes.Count > 0 do let (KeyValue(t, c)) = classes |> Seq.head classes.Remove t |> ignore - packageClass c + packageClass c t.Value.FullName if isBundle then match current.EntryPoint with @@ -209,12 +209,15 @@ let packageAssembly (refMeta: M.Info) (current: M.Info) isBundle = Application(Function([], Block allStatements), [], false, Some 0) let exprToString asmName pref sourceMap statement = + let env = WebSharper.Compiler.JavaScriptWriter.Environment.New(asmName, pref) let program = statement |> JavaScriptWriter.transformExpr (WebSharper.Compiler.JavaScriptWriter.Environment.New(asmName, pref)) |> WebSharper.Core.JavaScript.Syntax.Ignore |> WebSharper.Core.JavaScript.Syntax.Action |> fun x -> [ x ] + if env.ScopeFuncs.Count > 0 then + failwith "Unexpected top level function declaration found" let w = WebSharper.Core.JavaScript.Writer.CodeWriter(?assemblyName = if sourceMap then Some asmName else None) WebSharper.Core.JavaScript.Writer.WriteProgram pref w program w.GetCodeFile(), w.GetMapFile() diff --git a/src/compiler/WebSharper.Compiler/Translator.fs b/src/compiler/WebSharper.Compiler/Translator.fs index dca2f48dd..457f281ae 100644 --- a/src/compiler/WebSharper.Compiler/Translator.fs +++ b/src/compiler/WebSharper.Compiler/Translator.fs @@ -508,7 +508,6 @@ type DotNetToJavaScript private (comp: Compilation, ?inProgress) = if comp.HasGraph then this.AddMethodDependency(typ.Entity, meth.Entity) match info with - | M.Abstract name | M.Instance name -> match baseCall with | Some true -> @@ -706,7 +705,6 @@ type DotNetToJavaScript private (comp: Compilation, ?inProgress) = match info with | M.Static address -> GlobalAccess address - | M.Abstract name | M.Instance name -> match comp.TryLookupClassInfo typ.Entity with | Some { Address = Some addr } -> diff --git a/src/compiler/WebSharper.Core.JavaScript/Runtime.js b/src/compiler/WebSharper.Core.JavaScript/Runtime.js index e6051315d..bf36245e0 100644 --- a/src/compiler/WebSharper.Core.JavaScript/Runtime.js +++ b/src/compiler/WebSharper.Core.JavaScript/Runtime.js @@ -39,7 +39,7 @@ IntelliFactory = { }; }, - Class: function (members, base, statics) { + Class: function (members, base, statics, name) { var proto = base ? new base() : {}; var typeFunction = function (copyFrom) { if (copyFrom) { @@ -48,6 +48,7 @@ IntelliFactory = { } for (var m in members) { proto[m] = members[m] } typeFunction.prototype = proto; + typeFunction.name = name; if (statics) { for (var f in statics) { typeFunction[f] = statics[f] } } @@ -249,6 +250,10 @@ IntelliFactory = { return f; }, + PipeApply: function (x, f, args) { + return IntelliFactory.Runtime.Apply(f, args?args.concat([x]):[x]); + }, + UnionByType: function (types, value, optional) { var vt = typeof value; for (var i = 0; i < types.length; i++) { diff --git a/src/compiler/WebSharper.Core/AST.fs b/src/compiler/WebSharper.Core/AST.fs index 7ea891c15..30ce02b6b 100644 --- a/src/compiler/WebSharper.Core/AST.fs +++ b/src/compiler/WebSharper.Core/AST.fs @@ -204,6 +204,8 @@ and Statement = | Block of Statements:list /// Variable declaration | VarDeclaration of Variable:Id * Value:Expression + /// Function declaration + | FuncDeclaration of FuncId:Id * Parameters:list * Body:Statement /// 'while' loop | While of Condition:Expression * Body:Statement /// 'do..while' loop @@ -421,6 +423,9 @@ type Transformer() = /// Variable declaration abstract TransformVarDeclaration : Variable:Id * Value:Expression -> Statement override this.TransformVarDeclaration (a, b) = VarDeclaration (this.TransformId a, this.TransformExpression b) + /// Function declaration + abstract TransformFuncDeclaration : FuncId:Id * Parameters:list * Body:Statement -> Statement + override this.TransformFuncDeclaration (a, b, c) = FuncDeclaration (this.TransformId a, List.map this.TransformId b, this.TransformStatement c) /// 'while' loop abstract TransformWhile : Condition:Expression * Body:Statement -> Statement override this.TransformWhile (a, b) = While (this.TransformExpression a, this.TransformStatement b) @@ -537,6 +542,7 @@ type Transformer() = | Return a -> this.TransformReturn a | Block a -> this.TransformBlock a | VarDeclaration (a, b) -> this.TransformVarDeclaration (a, b) + | FuncDeclaration (a, b, c) -> this.TransformFuncDeclaration (a, b, c) | While (a, b) -> this.TransformWhile (a, b) | DoWhile (a, b) -> this.TransformDoWhile (a, b) | For (a, b, c, d) -> this.TransformFor (a, b, c, d) @@ -739,6 +745,9 @@ type Visitor() = /// Variable declaration abstract VisitVarDeclaration : Variable:Id * Value:Expression -> unit override this.VisitVarDeclaration (a, b) = this.VisitId a; this.VisitExpression b + /// Function declaration + abstract VisitFuncDeclaration : FuncId:Id * Parameters:list * Body:Statement -> unit + override this.VisitFuncDeclaration (a, b, c) = this.VisitId a; List.iter this.VisitId b; this.VisitStatement c /// 'while' loop abstract VisitWhile : Condition:Expression * Body:Statement -> unit override this.VisitWhile (a, b) = this.VisitExpression a; this.VisitStatement b @@ -853,6 +862,7 @@ type Visitor() = | Return a -> this.VisitReturn a | Block a -> this.VisitBlock a | VarDeclaration (a, b) -> this.VisitVarDeclaration (a, b) + | FuncDeclaration (a, b, c) -> this.VisitFuncDeclaration (a, b, c) | While (a, b) -> this.VisitWhile (a, b) | DoWhile (a, b) -> this.VisitDoWhile (a, b) | For (a, b, c, d) -> this.VisitFor (a, b, c, d) @@ -941,6 +951,7 @@ module IgnoreSourcePos = let (|Return|_|) x = match ignoreStatementSourcePos x with Return a -> Some a | _ -> None let (|Block|_|) x = match ignoreStatementSourcePos x with Block a -> Some a | _ -> None let (|VarDeclaration|_|) x = match ignoreStatementSourcePos x with VarDeclaration (a, b) -> Some (a, b) | _ -> None + let (|FuncDeclaration|_|) x = match ignoreStatementSourcePos x with FuncDeclaration (a, b, c) -> Some (a, b, c) | _ -> None let (|While|_|) x = match ignoreStatementSourcePos x with While (a, b) -> Some (a, b) | _ -> None let (|DoWhile|_|) x = match ignoreStatementSourcePos x with DoWhile (a, b) -> Some (a, b) | _ -> None let (|For|_|) x = match ignoreStatementSourcePos x with For (a, b, c, d) -> Some (a, b, c, d) | _ -> None diff --git a/src/compiler/WebSharper.Core/Metadata.fs b/src/compiler/WebSharper.Core/Metadata.fs index 0d73bf149..6cea8e253 100644 --- a/src/compiler/WebSharper.Core/Metadata.fs +++ b/src/compiler/WebSharper.Core/Metadata.fs @@ -109,7 +109,6 @@ type ParameterObject = type CompiledMember = | Instance of string - | Abstract of string | Static of Address | Constructor of Address | Inline diff --git a/src/compiler/WebSharper.Core/genAST.fsx b/src/compiler/WebSharper.Core/genAST.fsx index f0251c02f..6ac3fcf30 100644 --- a/src/compiler/WebSharper.Core/genAST.fsx +++ b/src/compiler/WebSharper.Core/genAST.fsx @@ -226,7 +226,8 @@ let StatementDefs = , "Block of statements" "VarDeclaration", [ Id, "variable"; Expr, "value" ] , "Variable declaration" -// "FuncDeclaration", [ Id, "funcId"; List Id, "parameters"; Statement, "body" ] + "FuncDeclaration", [ Id, "funcId"; List Id, "parameters"; Statement, "body" ] + , "Function declaration" "While", [ Expr, "condition"; Statement, "body" ] , "'while' loop" "DoWhile", [ Statement, "body"; Expr, "condition" ]