jsaddle 0.8.3.2 → 0.9.0.0
raw patch · 7 files changed
+240/−75 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Language.Javascript.JSaddle.Monad: class (Applicative m, MonadIO m) => MonadJSM m
- Language.Javascript.JSaddle.Native.Internal: newCallback :: JSCallAsFunction -> JSM Object
- Language.Javascript.JSaddle.Run: NewCallback :: JSValueForSend -> AsyncCommand
- Language.Javascript.JSaddle.Types: NewCallback :: JSValueForSend -> AsyncCommand
- Language.Javascript.JSaddle.Types: class (Applicative m, MonadIO m) => MonadJSM m
- Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Reader.ReaderT e m)
- Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.State.Lazy.StateT r m)
- Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.State.Strict.StateT r m)
+ Language.Javascript.JSaddle.Monad: class (Applicative m, MonadIO m) => MonadJSM m where liftJSM' = lift . (liftJSM' :: MonadJSM m' => JSM a -> m' a)
+ Language.Javascript.JSaddle.Native.Internal: newAsyncCallback :: JSCallAsFunction -> JSM Object
+ Language.Javascript.JSaddle.Native.Internal: newSyncCallback :: JSCallAsFunction -> JSM Object
+ Language.Javascript.JSaddle.Object: asyncFunction :: JSCallAsFunction -> JSM Function
+ Language.Javascript.JSaddle.Run: EndSyncBlock :: AsyncCommand
+ Language.Javascript.JSaddle.Run: NewAsyncCallback :: JSValueForSend -> AsyncCommand
+ Language.Javascript.JSaddle.Run: NewSyncCallback :: JSValueForSend -> AsyncCommand
+ Language.Javascript.JSaddle.Run: StartSyncBlock :: AsyncCommand
+ Language.Javascript.JSaddle.Types: BatchResults :: Int -> BatchResults -> Results
+ Language.Javascript.JSaddle.Types: Duplicate :: Int -> Int -> Results
+ Language.Javascript.JSaddle.Types: EndSyncBlock :: AsyncCommand
+ Language.Javascript.JSaddle.Types: NewAsyncCallback :: JSValueForSend -> AsyncCommand
+ Language.Javascript.JSaddle.Types: NewSyncCallback :: JSValueForSend -> AsyncCommand
+ Language.Javascript.JSaddle.Types: StartSyncBlock :: AsyncCommand
+ Language.Javascript.JSaddle.Types: class (Applicative m, MonadIO m) => MonadJSM m where liftJSM' = lift . (liftJSM' :: MonadJSM m' => JSM a -> m' a)
+ Language.Javascript.JSaddle.Types: data BatchResults
+ Language.Javascript.JSaddle.Types: instance (Control.Monad.Trans.Error.Error e, Language.Javascript.JSaddle.Types.MonadJSM m) => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Error.ErrorT e m)
+ Language.Javascript.JSaddle.Types: instance (GHC.Base.Monoid w, Language.Javascript.JSaddle.Types.MonadJSM m) => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.RWS.Lazy.RWST r w s m)
+ Language.Javascript.JSaddle.Types: instance (GHC.Base.Monoid w, Language.Javascript.JSaddle.Types.MonadJSM m) => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.RWS.Strict.RWST r w s m)
+ Language.Javascript.JSaddle.Types: instance (GHC.Base.Monoid w, Language.Javascript.JSaddle.Types.MonadJSM m) => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Writer.Lazy.WriterT w m)
+ Language.Javascript.JSaddle.Types: instance (GHC.Base.Monoid w, Language.Javascript.JSaddle.Types.MonadJSM m) => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Writer.Strict.WriterT w m)
+ Language.Javascript.JSaddle.Types: instance Data.Aeson.Types.FromJSON.FromJSON Language.Javascript.JSaddle.Types.BatchResults
+ Language.Javascript.JSaddle.Types: instance Data.Aeson.Types.ToJSON.ToJSON Language.Javascript.JSaddle.Types.BatchResults
+ Language.Javascript.JSaddle.Types: instance GHC.Generics.Generic Language.Javascript.JSaddle.Types.BatchResults
+ Language.Javascript.JSaddle.Types: instance GHC.Show.Show Language.Javascript.JSaddle.Types.BatchResults
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Cont.ContT r m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Except.ExceptT e m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Identity.IdentityT m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.List.ListT m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Maybe.MaybeT m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.Reader.ReaderT r m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.State.Lazy.StateT s m)
+ Language.Javascript.JSaddle.Types: instance Language.Javascript.JSaddle.Types.MonadJSM m => Language.Javascript.JSaddle.Types.MonadJSM (Control.Monad.Trans.State.Strict.StateT s m)
- Language.Javascript.JSaddle.Run: runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), IO ())
+ Language.Javascript.JSaddle.Run: runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results -> IO Batch, IO ())
- Language.Javascript.JSaddle.Run.Files: runBatch :: (ByteString -> ByteString) -> ByteString
+ Language.Javascript.JSaddle.Run.Files: runBatch :: (ByteString -> ByteString) -> Maybe (ByteString -> ByteString) -> ByteString
- Language.Javascript.JSaddle.Types: Batch :: [Either AsyncCommand Command] -> Bool -> Batch
+ Language.Javascript.JSaddle.Types: Batch :: [Either AsyncCommand Command] -> Bool -> Int -> Batch
- Language.Javascript.JSaddle.Types: Callback :: JSValueReceived -> JSValueReceived -> [JSValueReceived] -> Results
+ Language.Javascript.JSaddle.Types: Callback :: Int -> BatchResults -> JSValueReceived -> JSValueReceived -> [JSValueReceived] -> Results
- Language.Javascript.JSaddle.Types: Failure :: [Result] -> JSValueReceived -> Results
+ Language.Javascript.JSaddle.Types: Failure :: [Result] -> JSValueReceived -> BatchResults
- Language.Javascript.JSaddle.Types: Success :: [Result] -> Results
+ Language.Javascript.JSaddle.Types: Success :: [Result] -> BatchResults
- Language.Javascript.JSaddle.Types: liftJSM' :: MonadJSM m => JSM a -> m a
+ Language.Javascript.JSaddle.Types: liftJSM' :: (MonadJSM m, MonadJSM m', MonadTrans t) => JSM a' -> t m' a'
Files
- jsaddle.cabal +1/−1
- src-ghc/JavaScript/Array/Internal.hs +0/−2
- src/Language/Javascript/JSaddle/Native/Internal.hs +15/−6
- src/Language/Javascript/JSaddle/Object.hs +29/−6
- src/Language/Javascript/JSaddle/Run.hs +56/−25
- src/Language/Javascript/JSaddle/Run/Files.hs +90/−14
- src/Language/Javascript/JSaddle/Types.hs +49/−21
jsaddle.cabal view
@@ -1,5 +1,5 @@ name: jsaddle-version: 0.8.3.2+version: 0.9.0.0 cabal-version: >=1.10 build-type: Simple license: MIT
src-ghc/JavaScript/Array/Internal.hs view
@@ -21,8 +21,6 @@ import Language.Javascript.JSaddle.Types (JSM, SomeJSArray(..), JSArray, MutableJSArray, STJSArray, Object(..), GHCJSPure(..)) import Language.Javascript.JSaddle.Native.Internal (newArray, getPropertyByName, getPropertyAtIndex, callAsFunction, valueToNumber)-import Language.Javascript.JSaddle.Run- (Command(..), Result(..), AsyncCommand(..), sendCommand, sendLazyCommand) create :: JSM MutableJSArray create = SomeJSArray <$> newArray []
src/Language/Javascript/JSaddle/Native/Internal.hs view
@@ -27,7 +27,8 @@ , callAsFunction , callAsConstructor , newEmptyObject- , newCallback+ , newAsyncCallback+ , newSyncCallback , newArray , evaluateScript , deRefVal@@ -54,7 +55,7 @@ JSStringReceived(..), JSStringForSend(..), JSObjectForSend(..)) import Language.Javascript.JSaddle.Monad (askJSM) import Language.Javascript.JSaddle.Run- (Command(..), AsyncCommand(..), Result(..), sendCommand,+ (Command(..), Result(..), sendCommand, sendAsyncCommand, sendLazyCommand, wrapJSVal) wrapJSString :: MonadIO m => JSStringReceived -> m JSString@@ -136,13 +137,21 @@ newEmptyObject = Object <$> sendLazyCommand NewEmptyObject {-# INLINE newEmptyObject #-} -newCallback :: JSCallAsFunction -> JSM Object-newCallback f = do- object <- Object <$> sendLazyCommand NewCallback+newAsyncCallback :: JSCallAsFunction -> JSM Object+newAsyncCallback f = do+ object <- Object <$> sendLazyCommand NewAsyncCallback add <- addCallback <$> askJSM liftIO $ add object f return object-{-# INLINE newCallback #-}+{-# INLINE newAsyncCallback #-}++newSyncCallback :: JSCallAsFunction -> JSM Object+newSyncCallback f = do+ object <- Object <$> sendLazyCommand NewSyncCallback+ add <- addCallback <$> askJSM+ liftIO $ add object f+ return object+{-# INLINE newSyncCallback #-} newArray :: [JSVal] -> JSM JSVal newArray xs = withJSVals xs $ \xs' -> sendLazyCommand (NewArray xs')
src/Language/Javascript/JSaddle/Object.hs view
@@ -66,6 +66,7 @@ -- * Calling Haskell From JavaScript , Function(..) , function+ , asyncFunction , freeFunction , fun , JSCallAsFunction@@ -102,7 +103,7 @@ #ifdef ghcjs_HOST_OS import GHCJS.Types (nullRef) import GHCJS.Foreign.Callback- (releaseCallback, syncCallback2, OnBlocked(..), Callback)+ (releaseCallback, syncCallback2, asyncCallback2, OnBlocked(..), Callback) import GHCJS.Marshal (ToJSVal(..)) import JavaScript.Array (MutableJSArray) import qualified JavaScript.Array as Array (toListIO, fromListIO)@@ -115,7 +116,7 @@ #else import GHCJS.Marshal.Internal (ToJSVal(..)) import Language.Javascript.JSaddle.Native- (newCallback, callAsFunction, callAsConstructor)+ (newAsyncCallback, newSyncCallback, callAsFunction, callAsConstructor) import Language.Javascript.JSaddle.Monad (askJSM, JSM) import Language.Javascript.JSaddle.Types (JSString, Object(..), SomeJSArray(..),@@ -435,9 +436,16 @@ #endif +#ifdef ghcjs_HOST_OS+foreign import javascript unsafe "$r = function () { $1(this, arguments); }"+ makeFunctionWithCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object+#endif+ -- ^ Make a JavaScript function object that wraps a Haskell function.+-- Calls made to the function will be synchronous where possible+-- (on GHCJS it uses on `syncCallback2` with `ContinueAsync`). function :: JSCallAsFunction -- ^ Haskell function to call- -> JSM Function -- ^ Returns a JavaScript function object that will+ -> JSM Function -- ^ Returns a JavaScript function object that will -- call the Haskell one when it is called #ifdef ghcjs_HOST_OS function f = do@@ -445,11 +453,26 @@ rargs <- Array.toListIO (coerce args) f this this rargs -- TODO pass function object through Function callback <$> makeFunctionWithCallback callback-foreign import javascript unsafe "$r = function () { $1(this, arguments); }"- makeFunctionWithCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object #else function f = do- object <- newCallback f+ object <- newSyncCallback f+ return $ Function object+#endif++-- ^ Make a JavaScript function object that wraps a Haskell function.+-- Calls made to the function will be Asynchronous.+asyncFunction :: JSCallAsFunction -- ^ Haskell function to call+ -> JSM Function -- ^ Returns a JavaScript function object that will+ -- call the Haskell one when it is called+#ifdef ghcjs_HOST_OS+asyncFunction f = do+ callback <- asyncCallback2 $ \this args -> do+ rargs <- Array.toListIO (coerce args)+ f this this rargs -- TODO pass function object through+ Function callback <$> makeFunctionWithCallback callback+#else+asyncFunction f = do+ object <- newAsyncCallback f return $ Function object #endif
src/Language/Javascript/JSaddle/Run.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}@@ -49,7 +50,7 @@ import Control.Concurrent.STM.TVar (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO) import Control.Concurrent.MVar- (MVar, MVar, putMVar, takeMVar, newEmptyMVar)+ (tryTakeMVar, MVar, putMVar, takeMVar, newEmptyMVar, readMVar) import System.IO.Unsafe (unsafeInterleaveIO) import System.Mem.Weak (addFinalizer)@@ -61,12 +62,14 @@ import Data.IORef (newIORef, atomicWriteIORef, readIORef) import Language.Javascript.JSaddle.Types- (Command(..), AsyncCommand(..), Result(..), Results(..), JSContextRef(..), JSVal(..),+ (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..), Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..)) import Language.Javascript.JSaddle.Exception (JSException(..)) -- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal) import Control.DeepSeq (deepseq) import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))+import Data.Maybe (isJust)+import Data.Foldable (forM_) #endif -- | Enable (or disable) JSaddle logging@@ -146,10 +149,11 @@ s <- doSendAsyncCommand <$> JSM ask liftIO $ s cmd -runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), IO ())+runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results -> IO Batch, IO ()) runJavaScript sendBatch entryPoint = do startTime' <- getCurrentTime recvMVar <- newEmptyMVar+ lastAsyncBatch <- newEmptyMVar commandChan <- newTChanIO callbacks <- newTVarIO M.empty nextRef' <- newTVarIO 0@@ -169,17 +173,33 @@ , nextRef = nextRef' , doEnableLogging = atomicWriteIORef loggingEnabled }- let processResults = \case+ let processResults :: Bool -> Results -> IO ()+ processResults syncCallbacks = \case (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err- (Callback f this a) -> do- logInfo ("Call " <>)+ (Callback n br f this a) -> do+ putMVar recvMVar (n, br) f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx this' <- runReaderT (unJSM $ wrapJSVal this) ctx args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx+ logInfo (("Call " <> show fNumber <> " ") <>) (M.lookup fNumber <$> liftIO (readTVarIO callbacks)) >>= \case Nothing -> liftIO $ putStrLn "Callback called after it was freed"- Just cb -> void . forkIO $ runReaderT (unJSM $ cb f' this' args) ctx- m -> putMVar recvMVar m+ Just cb -> void . forkIO $ do+ runReaderT (unJSM $ cb f' this' args) ctx+ when syncCallbacks $+ doSendAsyncCommand ctx EndSyncBlock+ Duplicate nBatch nExpected -> do+ putStrLn $ "Error : Unexpected Duplicate. syncCallbacks=" <> show syncCallbacks <>+ " nBatch=" <> show nBatch <> " nExpected=" <> show nExpected+ void $ doSendCommand ctx Sync+ BatchResults n br -> putMVar recvMVar (n, br)+ asyncResults :: Results -> IO ()+ asyncResults results =+ void . forkIO $ processResults False results+ syncResults :: Results -> IO Batch+ syncResults results = do+ void . forkIO $ processResults True results+ readMVar lastAsyncBatch logInfo s = readIORef loggingEnabled >>= \case True -> do@@ -189,23 +209,34 @@ cbCount <- M.size <$> readTVarIO callbacks putStrLn . s $ "M " <> currentBytesUsedStr <> "CB " <> show cbCount False -> return ()- _ <- forkIO . forever $ readBatch commandChan >>= \case- (batch@(Batch cmds _), resultMVars) -> do+ _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->+ readBatch nBatch commandChan >>= \case+ (batch@(Batch cmds _ _), resultMVars) -> do logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))+ _ <- tryTakeMVar lastAsyncBatch+ putMVar lastAsyncBatch batch sendBatch batch- takeMVar recvMVar >>= \case- Success results | length results /= length resultMVars -> error "Unexpected number of jsaddle results"- | otherwise -> zipWithM_ putMVar resultMVars results- Failure results exception -> do- -- The exception will only be rethrown in Haskell if/when one of the- -- missing results (if any) is evaluated.- putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"- zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)- _ -> error "Unexpected jsaddle results"- return (processResults, runReaderT (unJSM entryPoint) ctx)+ takeResult recvMVar nBatch >>= \case+ (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"+ (_, Success results) | length results /= length resultMVars -> error "Unexpected number of jsaddle results"+ | otherwise -> zipWithM_ putMVar resultMVars results+ (_, Failure results exception) -> do+ -- The exception will only be rethrown in Haskell if/when one of the+ -- missing results (if any) is evaluated.+ putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"+ zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)+ return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx) where- readBatch :: TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])- readBatch chan = do+ numberForeverFromM_ :: (Monad m, Enum n) => n -> (n -> m a) -> m ()+ numberForeverFromM_ !n f = do+ f n+ numberForeverFromM_ (succ n) f+ takeResult recvMVar nBatch =+ takeMVar recvMVar >>= \case+ (n, _) | n < nBatch -> takeResult recvMVar nBatch+ r -> return r+ readBatch :: Int -> TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])+ readBatch nBatch chan = do first <- atomically $ readTChan chan -- We want at least one command to send loop first ([], []) where@@ -216,17 +247,17 @@ let cmds = Right syncCmd:cmds' resultMVars = resultMVar:resultMVars' atomically (tryReadTChan chan) >>= \case- Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)+ Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars) Just cmd -> loop cmd (cmds, resultMVars) loop (Left asyncCmd) (cmds', resultMVars) = do let cmds = Left asyncCmd:cmds' atomically (tryReadTChan chan) >>= \case- Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)+ Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars) Just cmd -> loop cmd (cmds, resultMVars) -- When we have seen a SyncWithAnimationFrame command only a synchronous command should end the batch loopAnimation :: Either AsyncCommand (Command, MVar Result) -> ([Either AsyncCommand Command], [MVar Result]) -> IO (Batch, [MVar Result]) loopAnimation (Right (Sync, resultMVar)) (cmds, resultMVars) =- return (Batch (reverse (Right Sync:cmds)) True, reverse (resultMVar:resultMVars))+ return (Batch (reverse (Right Sync:cmds)) True nBatch, reverse (resultMVar:resultMVars)) loopAnimation (Right (syncCmd, resultMVar)) (cmds, resultMVars) = atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Right syncCmd:cmds, resultMVar:resultMVars) loopAnimation (Left asyncCmd) (cmds, resultMVars) =
src/Language/Javascript/JSaddle/Run/Files.hs view
@@ -43,13 +43,23 @@ \ jsaddle_values.set(3, true);\n\ \ jsaddle_values.set(4, window);\n\ \ var jsaddle_index = 100;\n\+ \ var expectedBatch = 1;\n\+ \ var lastResults = [0, {}];\n\+ \ var inCallback = 0;\n\ \" -runBatch :: (ByteString -> ByteString) -> ByteString-runBatch send = "\+runBatch :: (ByteString -> ByteString) -> Maybe (ByteString -> ByteString) -> ByteString+runBatch send sendSync = "\+ \ var runBatch = function(firstBatch, initialSyncDepth) {\n\ \ var processBatch = function(timestamp) {\n\- \ var results = [];\n\- \ try {\n\+ \ var batch = firstBatch;\n\+ \ var results = [];\n\+ \ inCallback++;\n\+ \ try {\n\+ \ syncDepth = initialSyncDepth || 0;\n\+ \ for(;;){\n\+ \ if(batch[2] === expectedBatch) {\n\+ \ expectedBatch++;\n\ \ var nCommandsLength = batch[0].length;\n\ \ for (var nCommand = 0; nCommand != nCommandsLength; nCommand++) {\n\ \ var cmd = batch[0][nCommand];\n\@@ -89,7 +99,7 @@ \ var n = d.contents;\n\ \ jsaddle_values.set(n, {});\n\ \ break;\n\- \ case \"NewCallback\":\n\+ \ case \"NewAsyncCallback\":\n\ \ (function() {\n\ \ var nFunction = d.contents;\n\ \ jsaddle_values.set(nFunction, function() {\n\@@ -101,9 +111,33 @@ \ jsaddle_values.set(nArg, arguments[i]);\n\ \ args[i] = nArg;\n\ \ }\n\- \ " <> send "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> "\n\+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n\ \ })})();\n\ \ break;\n\+ \ case \"NewSyncCallback\":\n\+ \ (function() {\n\+ \ var nFunction = d.contents;\n\+ \ jsaddle_values.set(nFunction, function() {\n\+ \ var nThis = ++jsaddle_index;\n\+ \ jsaddle_values.set(nThis, this);\n\+ \ var args = [];\n\+ \ for (var i = 0; i != arguments.length; i++) {\n\+ \ var nArg = ++jsaddle_index;\n\+ \ jsaddle_values.set(nArg, arguments[i]);\n\+ \ args[i] = nArg;\n\+ \ }\n" <> (+ case sendSync of+ Just s ->+ " if(inCallback > 0) {\n\+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n\+ \ } else {\n\+ \ runBatch(" <> s "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> ", 1);\n\+ \ }\n"+ Nothing ->+ " " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nThis, args]}" <> "\n"+ ) <>+ " })})();\n\+ \ break;\n\ \ case \"CallAsFunction\":\n\ \ var n = d.contents[3];\n\ \ jsaddle_values.set(n,\n\@@ -148,6 +182,12 @@ \ var n = d.contents[0];\n\ \ jsaddle_values.set(n, timestamp);\n\ \ break;\n\+ \ case \"StartSyncBlock\":\n\+ \ syncDepth++;\n\+ \ break;\n\+ \ case \"EndSyncBlock\":\n\+ \ syncDepth--;\n\+ \ break;\n\ \ default:\n\ \ " <> send "{\"tag\": \"ProtocolError\", \"contents\": e.data}" <> "\n\ \ return;\n\@@ -210,20 +250,56 @@ \ }\n\ \ }\n\ \ }\n\- \ " <> send "{\"tag\": \"Success\", \"contents\": results}" <> "\n\- \ }\n\- \ catch (err) {\n\- \ var n = ++jsaddle_index;\n\- \ jsaddle_values.set(n, err);\n\- \ " <> send "{\"tag\": \"Failure\", \"contents\": [results,n]}" <> "\n\+ \ if(syncDepth <= 0) {\n\+ \ lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": results}];\n\+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> "\n\+ \ break;\n\+ \ } else {\n" <> (+ case sendSync of+ Just s ->+ " lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": results}];\n\+ \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\+ \ results = [];\n"+ Nothing ->+ " " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Success\", \"contents\": results}]}" <> "\n\+ \ break;\n"+ ) <>+ " }\n\+ \ } else {\n\+ \ if(syncDepth <= 0) {\n\+ \ break;\n\+ \ } else {\n" <> (+ case sendSync of+ Just s ->+ " if(batch[2] === expectedBatch - 1) {\n\+ \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\+ \ } else {\n\+ \ batch = " <> s "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> ";\n\+ \ }\n\+ \ results = [];\n"+ Nothing ->+ " " <> send "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> "\n\+ \ break;\n"+ ) <>+ " }\n\+ \ }\n\ \ }\n\+ \ }\n\+ \ catch (err) {\n\+ \ var n = ++jsaddle_index;\n\+ \ jsaddle_values.set(n, err);\n\+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [results, n]}]}" <> "\n\+ \ }\n\+ \ inCallback--;\n\ \ };\n\- \ if(batch[1]) {\n\+ \ if(batch[1] && (initialSyncDepth || 0) === 0) {\n\ \ window.requestAnimationFrame(processBatch);\n\ \ }\n\ \ else {\n\ \ processBatch(window.performance ? window.performance.now() : null);\n\ \ }\n\+ \ };\n\+ \ runBatch(batch);\n\ \" -- Use this to generate this string for embedding@@ -249,7 +325,7 @@ \ ws.onmessage = function(e) {\n\ \ var batch = JSON.parse(e.data);\n\ \\n\- \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));") <> "\+ \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));") Nothing <> "\ \ };\n\ \ };\n\ \ ws.onerror = function() {\n\
src/Language/Javascript/JSaddle/Types.hs view
@@ -14,6 +14,7 @@ #endif {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE AllowAmbiguousTypes #-} ----------------------------------------------------------------------------- -- -- Module : Language.Javascript.JSaddle.Types@@ -73,6 +74,7 @@ , Command(..) , Batch(..) , Result(..)+ , BatchResults(..) , Results(..) #endif ) where@@ -87,10 +89,19 @@ import GHCJS.Prim.Internal (JSVal(..), JSValueRef) import Data.JSString.Internal.Type (JSString(..)) import Control.DeepSeq (NFData(..))+import Control.Monad.Trans.Cont (ContT(..))+import Control.Monad.Trans.Error (Error(..), ErrorT(..))+import Control.Monad.Trans.Except (ExceptT(..))+import Control.Monad.Trans.Identity (IdentityT(..))+import Control.Monad.Trans.List (ListT(..))+import Control.Monad.Trans.Maybe (MaybeT(..)) import Control.Monad.Trans.Reader (ReaderT(..))-import Control.Monad.Trans.State.Lazy (StateT(..))-import qualified Control.Monad.Trans.State.Strict as Strict- (StateT(..))+import Control.Monad.Trans.RWS.Lazy as Lazy (RWST(..))+import Control.Monad.Trans.RWS.Strict as Strict (RWST(..))+import Control.Monad.Trans.State.Lazy as Lazy (StateT(..))+import Control.Monad.Trans.State.Strict as Strict (StateT(..))+import Control.Monad.Trans.Writer.Lazy as Lazy (WriterT(..))+import Control.Monad.Trans.Writer.Strict as Strict (WriterT(..)) import Control.Monad.Trans.Class (MonadTrans(..)) import Control.Monad.Fix (MonadFix) import Control.Monad.Ref (MonadAtomicRef(..), MonadRef(..))@@ -191,23 +202,28 @@ #else class (Applicative m, MonadIO m) => MonadJSM m where liftJSM' :: JSM a -> m a- {-# MINIMAL liftJSM' #-} -instance MonadJSM JSM where- liftJSM' = id- {-# INLINE liftJSM' #-}--instance MonadJSM m => MonadJSM (ReaderT e m) where- liftJSM' = lift . liftJSM'+ default liftJSM' :: (MonadJSM m', MonadTrans t) => JSM a' -> t m' a'+ liftJSM' = lift . (liftJSM' :: MonadJSM m' => JSM a -> m' a) {-# INLINE liftJSM' #-} -instance MonadJSM m => MonadJSM (StateT r m) where- liftJSM' = lift . liftJSM'+instance MonadJSM JSM where+ liftJSM' = id {-# INLINE liftJSM' #-} -instance MonadJSM m => MonadJSM (Strict.StateT r m) where- liftJSM' = lift . liftJSM'- {-# INLINE liftJSM' #-}+instance (MonadJSM m) => MonadJSM (ContT r m)+instance (Error e, MonadJSM m) => MonadJSM (ErrorT e m)+instance (MonadJSM m) => MonadJSM (ExceptT e m)+instance (MonadJSM m) => MonadJSM (IdentityT m)+instance (MonadJSM m) => MonadJSM (ListT m)+instance (MonadJSM m) => MonadJSM (MaybeT m)+instance (MonadJSM m) => MonadJSM (ReaderT r m)+instance (Monoid w, MonadJSM m) => MonadJSM (Lazy.RWST r w s m)+instance (Monoid w, MonadJSM m) => MonadJSM (Strict.RWST r w s m)+instance (MonadJSM m) => MonadJSM (Lazy.StateT s m)+instance (MonadJSM m) => MonadJSM (Strict.StateT s m)+instance (Monoid w, MonadJSM m) => MonadJSM (Lazy.WriterT w m)+instance (Monoid w, MonadJSM m) => MonadJSM (Strict.WriterT w m) instance MonadRef JSM where type Ref JSM = Ref IO@@ -315,11 +331,14 @@ | CallAsFunction JSObjectForSend JSObjectForSend [JSValueForSend] JSValueForSend | CallAsConstructor JSObjectForSend [JSValueForSend] JSValueForSend | NewEmptyObject JSValueForSend- | NewCallback JSValueForSend+ | NewAsyncCallback JSValueForSend+ | NewSyncCallback JSValueForSend | NewArray [JSValueForSend] JSValueForSend | EvaluateScript JSStringForSend JSValueForSend | SyncWithAnimationFrame JSValueForSend- deriving (Show, Generic)+ | StartSyncBlock+ | EndSyncBlock+ deriving (Show, Generic) instance ToJSON AsyncCommand where toEncoding = genericToEncoding defaultOptions@@ -351,7 +370,7 @@ instance NFData Command -- | Batch of commands that can be sent together to the JavaScript context-data Batch = Batch [Either AsyncCommand Command] Bool+data Batch = Batch [Either AsyncCommand Command] Bool Int deriving (Show, Generic) instance ToJSON Batch where@@ -382,9 +401,18 @@ instance FromJSON Result -data Results = Success [Result]- | Failure [Result] JSValueReceived- | Callback JSValueReceived JSValueReceived [JSValueReceived]+data BatchResults = Success [Result]+ | Failure [Result] JSValueReceived+ deriving (Show, Generic)++instance ToJSON BatchResults where+ toEncoding = genericToEncoding defaultOptions++instance FromJSON BatchResults++data Results = BatchResults Int BatchResults+ | Duplicate Int Int+ | Callback Int BatchResults JSValueReceived JSValueReceived [JSValueReceived] | ProtocolError Text deriving (Show, Generic)