From 566b4f364fbd4d73d82f6ac74ddd03a729bb52ca Mon Sep 17 00:00:00 2001 From: Alfonso Garcia-Caro Date: Fri, 26 Jul 2019 16:45:50 +0200 Subject: [PATCH] Fix #1863: Binding event to a value --- src/Fable.Transforms/FSharp2Fable.Util.fs | 21 +++++++++++----- src/Fable.Transforms/FSharp2Fable.fs | 9 ++++++- tests/Main/DateTimeTests.fs | 30 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index f4c63046aa..6bbabefe76 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -415,16 +415,25 @@ module Patterns = /// This matches the boilerplate generated to wrap .NET events from F# let (|CreateEvent|_|) = function - | Call(Some(Call(None, createEvent,_,_, - [Lambda(_eventDelegate, Call(Some callee, addEvent,[],[],[Value _eventDelegate'])); - Lambda(_eventDelegate2, Call(Some _callee2, _removeEvent,[],[],[Value _eventDelegate2'])); - Lambda(_callback, NewDelegate(_, Lambda(_delegateArg0, Lambda(_delegateArg1, Application(Value _callback',[],[Value _delegateArg0'; Value _delegateArg1'])))))])), - memb, typArgs, methTypArgs, args) - when createEvent.FullName = Types.createEvent -> + | Call(None,createEvent,_,_, + [Lambda(_eventDelegate, Call(Some callee, addEvent,[],[],[Value _eventDelegate'])); + Lambda(_eventDelegate2, Call(Some _callee2, _removeEvent,[],[],[Value _eventDelegate2'])); + Lambda(_callback, NewDelegate(_, Lambda(_delegateArg0, Lambda(_delegateArg1, Application(Value _callback',[],[Value _delegateArg0'; Value _delegateArg1'])))))]) + when createEvent.FullName = Types.createEvent -> let eventName = addEvent.CompiledName.Replace("add_","") + Some (callee, eventName) + | _ -> None + + let (|CallCreateEvent|_|) = function + | Call(Some(CreateEvent(callee, eventName)), memb, typArgs, methTypArgs, args) -> Some (callee, eventName, memb, typArgs, methTypArgs, args) | _ -> None + let (|BindCreateEvent|_|) = function + | Let((var, CreateEvent(value, eventName)), body) -> + Some (var, value, eventName, body) + | _ -> None + let (|ConstructorCall|_|) = function | NewObject(baseCall, genArgs, baseArgs) -> Some(baseCall, genArgs, baseArgs) | Call(None, baseCall, genArgs1, genArgs2, baseArgs) when baseCall.IsConstructor -> diff --git a/src/Fable.Transforms/FSharp2Fable.fs b/src/Fable.Transforms/FSharp2Fable.fs index 6e6928ce0b..879be70542 100644 --- a/src/Fable.Transforms/FSharp2Fable.fs +++ b/src/Fable.Transforms/FSharp2Fable.fs @@ -367,7 +367,7 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = let body = Fable.Let([ident1, id1Expr], Fable.Let([ident2, id2Expr], restExpr)) return Fable.Let([tupleIdent, tupleExpr], body) - | CreateEvent (callee, eventName, memb, ownerGenArgs, membGenArgs, membArgs) -> + | CallCreateEvent (callee, eventName, memb, ownerGenArgs, membGenArgs, membArgs) -> let! callee = transformExpr com ctx callee let! args = transformExprList com ctx membArgs let callee = get None Fable.Any callee eventName @@ -375,6 +375,13 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr = let typ = makeType com ctx.GenericArgs fsExpr.Type return makeCallFrom com ctx (makeRangeFrom fsExpr) typ false genArgs (Some callee) args memb + | BindCreateEvent (var, value, eventName, body) -> + let! value = transformExpr com ctx value + let value = get None Fable.Any value eventName + let ctx, ident = putBindingInScope com ctx var value + let! body = transformExpr com ctx body + return Fable.Let([ident, value], body) + // TODO: Detect if it's ResizeArray and compile as FastIntegerForLoop? | ForOf (PutArgInScope com ctx (newContext, ident), value, body) -> let! value = transformExpr com ctx value diff --git a/tests/Main/DateTimeTests.fs b/tests/Main/DateTimeTests.fs index 753301e760..30d0fbb47f 100644 --- a/tests/Main/DateTimeTests.fs +++ b/tests/Main/DateTimeTests.fs @@ -543,4 +543,34 @@ let tests = equal 10 !res t.Stop() } + + testCaseAsync "Assigning an event to a variable works" <| fun () -> // See #1863 + let createTimerAndObservable timerInterval = + // setup a timer + let timer = new System.Timers.Timer(float timerInterval) + timer.AutoReset <- true + // events are automatically IObservable + let observable = timer.Elapsed + // return an async task + let task = async { + timer.Start() + do! Async.Sleep 200 + timer.Stop() + } + // return a async task and the observable + (task,observable) + // create the timer and the corresponding observable + let basicTimer2 , timerEventStream = createTimerAndObservable 50 + + let mutable acc = 1 + // register that everytime something happens on the + // event stream, print the time. + timerEventStream |> Observable.subscribe (fun _ -> + acc <- acc + 1) |>ignore + + async { + do! basicTimer2 + printfn "%i" acc + acc > 2 |> equal true + } ]