packages feed

jsaddle 0.9.3.0 → 0.9.4.0

raw patch · 10 files changed

+118/−65 lines, 10 filesdep ~basedep ~processdep ~timePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, process, time

API changes (from Hackage documentation)

- GHCJS.Prim.Internal: instance Data.Aeson.Types.FromJSON.FromJSON GHCJS.Prim.Internal.JSVal
- GHCJS.Prim.Internal: instance Data.Aeson.Types.ToJSON.ToJSON GHCJS.Prim.Internal.JSVal
- GHCJS.Prim.Internal: instance GHC.Show.Show GHCJS.Prim.Internal.JSVal
- Language.Javascript.JSaddle.Types: instance Data.Aeson.Types.FromJSON.FromJSON Language.Javascript.JSaddle.Types.Object
- Language.Javascript.JSaddle.Types: instance Data.Aeson.Types.ToJSON.ToJSON Language.Javascript.JSaddle.Types.Object
- Language.Javascript.JSaddle.Types: instance GHC.Show.Show Language.Javascript.JSaddle.Types.Object
+ Language.Javascript.JSaddle.Types: [liveRefs] :: JSContextRef -> MVar (Set Int64)
- GHCJS.Prim.Internal: JSVal :: JSValueRef -> JSVal
+ GHCJS.Prim.Internal: JSVal :: (IORef JSValueRef) -> JSVal
- GHCJS.Types: type Ref# = Int64
+ GHCJS.Types: type Ref# = IORef Int64
- Language.Javascript.JSaddle.Types: JSContextRef :: Int64 -> UTCTime -> (Command -> IO Result) -> (AsyncCommand -> IO ()) -> (Object -> JSCallAsFunction -> IO ()) -> TVar JSValueRef -> (Bool -> IO ()) -> MVar (Set Text) -> MVar [Double -> JSM ()] -> JSContextRef
+ Language.Javascript.JSaddle.Types: JSContextRef :: Int64 -> UTCTime -> (Command -> IO Result) -> (AsyncCommand -> IO ()) -> (Object -> JSCallAsFunction -> IO ()) -> TVar JSValueRef -> (Bool -> IO ()) -> MVar (Set Text) -> MVar [Double -> JSM ()] -> MVar (Set Int64) -> JSContextRef
- Language.Javascript.JSaddle.Types: JSVal :: JSValueRef -> JSVal
+ Language.Javascript.JSaddle.Types: JSVal :: (IORef JSValueRef) -> JSVal
- Language.Javascript.JSaddle.Types: liftJSM' :: (MonadJSM m, MonadJSM m', MonadTrans t) => JSM a' -> t m' a'
+ Language.Javascript.JSaddle.Types: liftJSM' :: (MonadJSM m, MonadJSM m', MonadTrans t, m ~ t m') => JSM a -> m a

Files

jsaddle.cabal view
@@ -1,5 +1,5 @@ name: jsaddle-version: 0.9.3.0+version: 0.9.4.0 cabal-version: >=1.10 build-type: Simple license: MIT@@ -41,12 +41,12 @@             filepath >=1.4.0.0 && <1.5,             ghc-prim,             http-types >=0.8.6 && <0.10,-            process >=1.2.3.0 && <1.5,+            process >=1.2.3.0 && <1.7,             random >= 1.1 && < 1.2,             ref-tf >=0.4.0.1 && <0.5,             scientific >=0.3 && <0.4,             stm >=2.4.4 && <2.5,-            time >=1.5.0.1 && <1.8,+            time >=1.5.0.1 && <1.9,             unordered-containers >=0.2 && <0.3,             vector >=0.10 && <0.13         exposed-modules:
src-ghc/GHCJS/Foreign/Internal.hs view
@@ -27,26 +27,28 @@        (valueToBool) import Data.Typeable (Typeable) import GHCJS.Prim (isNull, isUndefined)+import System.IO.Unsafe (unsafePerformIO)+import Data.IORef (newIORef)  jsTrue :: JSVal-jsTrue = JSVal 3-{-# INLINE jsTrue #-}+jsTrue = JSVal . unsafePerformIO $ newIORef 3+{-# NOINLINE jsTrue #-}  jsFalse :: JSVal-jsFalse = JSVal 2-{-# INLINE jsFalse #-}+jsFalse = JSVal . unsafePerformIO $ newIORef 2+{-# NOINLINE jsFalse #-}  jsNull :: JSVal-jsNull = JSVal 0-{-# INLINE jsNull #-}+jsNull = JSVal . unsafePerformIO $ newIORef 0+{-# NOINLINE jsNull #-}  toJSBool :: Bool -> JSVal-toJSBool b = JSVal $ if b then 3 else 2-{-# INLINE toJSBool #-}+toJSBool b = JSVal . unsafePerformIO . newIORef $ if b then 3 else 2+{-# NOINLINE toJSBool #-}  jsUndefined :: JSVal-jsUndefined = JSVal 1-{-# INLINE jsUndefined #-}+jsUndefined = JSVal . unsafePerformIO $ newIORef 1+{-# NOINLINE jsUndefined #-}  isTruthy :: JSVal -> GHCJSPure Bool isTruthy = GHCJSPure . valueToBool
src-ghc/GHCJS/Prim/Internal.hs view
@@ -17,6 +17,8 @@ import           Data.Aeson (ToJSON(..), FromJSON(..))  import qualified GHC.Exception as Ex+import Data.IORef (newIORef, IORef)+import System.IO.Unsafe (unsafePerformIO)  -- A reference to a particular JavaScript value inside the JavaScript context type JSValueRef = Int64@@ -25,7 +27,7 @@   JSVal is a boxed type that can be used as FFI   argument or result. -}-newtype JSVal = JSVal JSValueRef deriving(Show, ToJSON, FromJSON)+newtype JSVal = JSVal (IORef JSValueRef)  instance NFData JSVal where   rnf x = x `seq` ()@@ -49,8 +51,8 @@   return (JSException (unsafeCoerce ref) "")  jsNull :: JSVal-jsNull = JSVal 0-{-# INLINE jsNull #-}+jsNull = JSVal . unsafePerformIO $ newIORef 0+{-# NOINLINE jsNull #-}  {- | If a synchronous thread tries to do something that can only      be done asynchronously, and the thread is set up to not
src-ghc/GHCJS/Types.hs view
@@ -27,19 +27,21 @@ import GHC.Types import GHC.Prim import GHC.Ptr+import GHC.IORef+import GHC.IO.Unsafe (unsafePerformIO)  import Control.DeepSeq import Unsafe.Coerce -type Ref# = Int64+type Ref# = IORef Int64  mkRef :: Ref# -> JSVal mkRef = JSVal {-# INLINE mkRef #-}  nullRef :: JSVal-nullRef = JSVal 0-{-# INLINE nullRef #-}+nullRef = JSVal . unsafePerformIO $ newIORef 0+{-# NOINLINE nullRef #-}  --toPtr :: JSVal -> Ptr a --toPtr (JSVal x) = unsafeCoerce (Ptr' x 0#)
src/Language/Javascript/JSaddle/Native/Internal.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-} ----------------------------------------------------------------------------- -- -- Module      :  Language.Javascript.JSaddle.Native@@ -45,7 +48,6 @@ ) where  import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad.Primitive (touch)  import Data.Aeson (Value) @@ -57,30 +59,34 @@ import Language.Javascript.JSaddle.Run        (Command(..), Result(..), sendCommand,         sendAsyncCommand, sendLazyCommand, wrapJSVal)+import GHC.IORef (IORef(..), readIORef)+import GHC.STRef (STRef(..))+import GHC.IO (IO(..))+import GHC.Base (touch#)  wrapJSString :: MonadIO m => JSStringReceived -> m JSString wrapJSString (JSStringReceived ref) = return $ JSString ref +touchIORef :: IORef a -> IO ()+touchIORef (IORef (STRef r#)) = IO $ \s -> case touch# r# s of s' -> (# s', () #)+ withJSVal :: MonadIO m => JSVal -> (JSValueForSend -> m a) -> m a withJSVal (JSVal ref) f = do-    result <- f (JSValueForSend ref)-    liftIO $ touch ref+    result <- (f . JSValueForSend) =<< liftIO (readIORef ref)+    liftIO $ touchIORef ref     return result  withJSVals :: MonadIO m => [JSVal] -> ([JSValueForSend] -> m a) -> m a withJSVals v f =- do result <- f (map (\(JSVal ref) -> JSValueForSend ref) v)-    liftIO $ mapM_ touch v+ do result <- f =<< mapM (\(JSVal ref) -> liftIO $ JSValueForSend <$> readIORef ref) v+    liftIO $ mapM_ (\(JSVal ref) -> touchIORef ref) v     return result  withObject :: MonadIO m => Object -> (JSObjectForSend -> m a) -> m a withObject (Object o) f = withJSVal o (f . JSObjectForSend)  withJSString :: MonadIO m => JSString -> (JSStringForSend -> m a) -> m a-withJSString v@(JSString ref) f =- do result <- f (JSStringForSend ref)-    liftIO $ touch v-    return result+withJSString (JSString ref) f = f (JSStringForSend ref)  setPropertyByName :: JSString -> JSVal -> Object -> JSM () setPropertyByName name val this =@@ -166,13 +172,14 @@ {-# INLINE deRefVal #-}  valueToBool :: JSVal -> JSM Bool-valueToBool (JSVal 0) = return False -- null-valueToBool (JSVal 1) = return False -- undefined-valueToBool (JSVal 2) = return False -- false-valueToBool (JSVal 3) = return True  -- true-valueToBool v = withJSVal v $ \rval -> do-    ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)-    return result+valueToBool v@(JSVal ref) = liftIO (readIORef ref) >>= \case+    0 -> return False -- null+    1 -> return False -- undefined+    2 -> return False -- false+    3 -> return True  -- true+    _ -> withJSVal v $ \rval -> do+        ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)+        return result {-# INLINE valueToBool #-}  valueToNumber :: JSVal -> JSM Double@@ -201,17 +208,25 @@ {-# INLINE valueToJSONValue #-}  isNull :: JSVal -> JSM Bool-isNull (JSVal 0) = return True-isNull v = withJSVal v $ \rval -> do-    ~(IsNullResult result) <- sendCommand $ IsNull rval-    return result+isNull v@(JSVal ref) = liftIO (readIORef ref) >>= \case+    0 -> return True  -- null+    1 -> return False -- undefined+    2 -> return False -- false+    3 -> return False -- true+    _ -> withJSVal v $ \rval -> do+        ~(IsNullResult result) <- sendCommand $ IsNull rval+        return result {-# INLINE isNull #-}  isUndefined :: JSVal -> JSM Bool-isUndefined (JSVal 1) = return True-isUndefined v = withJSVal v $ \rval -> do-    ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval-    return result+isUndefined v@(JSVal ref) = liftIO (readIORef ref) >>= \case+    0 -> return False -- null+    1 -> return True  -- undefined+    2 -> return False -- false+    3 -> return False -- true+    _ -> withJSVal v $ \rval -> do+        ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval+        return result {-# INLINE isUndefined #-}  strictEqual :: JSVal -> JSVal -> JSM Bool
src/Language/Javascript/JSaddle/Object.hs view
@@ -132,6 +132,8 @@ import Control.Monad.IO.Class (MonadIO(..)) import Language.Javascript.JSaddle.Properties import Control.Lens (IndexPreservingGetter, to)+import Data.IORef (newIORef, readIORef)+import System.IO.Unsafe (unsafePerformIO)  -- $setup -- >>> import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)@@ -482,8 +484,16 @@ freeFunction (Function callback _) = liftIO $     releaseCallback callback #else-freeFunction (Function (Object (JSVal objectRef))) =-    sendAsyncCommand (FreeCallback (JSValueForSend objectRef))+freeFunction (Function (Object (JSVal objectRef))) = do+    -- By now the callback should ideally have been removed from whatever+    -- events it was added to.+    -- In case a call to the callback is still pending (perhaps just being sent+    -- on the JS side) we use FreeCallback to queue the callback to be freed when+    -- the next batch of results comes back fro JS.+    -- We are not using withJSVal to keep JS value "alive" because FreeCallback+    -- does not use the it.+    n <- liftIO $ readIORef objectRef+    sendAsyncCommand (FreeCallback (JSValueForSend n)) #endif  instance ToJSVal Function where@@ -522,7 +532,7 @@ foreign import javascript unsafe "$r = window"     js_window :: Object #else-global = Object (JSVal 4)+global = Object . JSVal . unsafePerformIO $ newIORef 4 #endif  -- | Get a list containing the property names present on a given object@@ -603,5 +613,5 @@ #ifdef ghcjs_HOST_OS nullObject = Object nullRef #else-nullObject = Object (JSVal 0)+nullObject = Object . JSVal . unsafePerformIO $ newIORef 0 #endif
src/Language/Javascript/JSaddle/Run.hs view
@@ -65,13 +65,14 @@ import qualified Data.Map as M (lookup, delete, insert, empty, size) import qualified Data.Set as S (empty, member, insert, delete) import Data.Time.Clock (getCurrentTime,diffUTCTime)-import Data.IORef (newIORef, atomicWriteIORef, readIORef)+import Data.IORef+       (mkWeakIORef, newIORef, atomicWriteIORef, readIORef)  import Language.Javascript.JSaddle.Types        (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),         Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..)) import Language.Javascript.JSaddle.Exception (JSException(..))-import Control.DeepSeq (deepseq)+import Control.DeepSeq (force, deepseq) import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..)) import Data.Foldable (forM_) #endif@@ -165,6 +166,7 @@     finalizerThreads' <- newMVar S.empty     animationFrameHandlers' <- newMVar []     loggingEnabled <- newIORef False+    liveRefs' <- newMVar S.empty     let ctx = JSContextRef {             contextId = contextId'           , startTime = startTime'@@ -173,14 +175,19 @@                 atomically $ writeTChan commandChan (Right (cmd, result))                 unsafeInterleaveIO $                     takeMVar result >>= \case-                        (ThrowJSValue (JSValueReceived v)) -> throwIO $ JSException (JSVal v)+                        (ThrowJSValue v) -> do+                            jsval <- wrapJSVal' ctx v+                            throwIO $ JSException jsval                         r -> return r           , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)-          , addCallback = \(Object (JSVal val)) cb -> atomically $ modifyTVar' callbacks (M.insert val cb)+          , addCallback = \(Object (JSVal ioref)) cb -> do+                val <- readIORef ioref+                atomically $ modifyTVar' callbacks (M.insert val cb)           , nextRef = nextRef'           , doEnableLogging = atomicWriteIORef loggingEnabled           , finalizerThreads = finalizerThreads'           , animationFrameHandlers = animationFrameHandlers'+          , liveRefs = liveRefs'           }         processResults :: Bool -> Results -> IO ()         processResults syncCallbacks = \case@@ -282,17 +289,29 @@ addThreadFinalizer t@(ThreadId t#) (IO finalizer) =     IO $ \s -> case mkWeak# t# t finalizer s of { (# s1, _ #) -> (# s1, () #) } + wrapJSVal :: JSValueReceived -> JSM JSVal-wrapJSVal (JSValueReceived ref) = do-    -- TODO make sure this ref has not already been wrapped (perhaps only in debug version)-    let result = JSVal ref-    when (ref >= 5 || ref < 0) $ do-        ctx <- JSM ask-        liftIO . addFinalizer ref $ do+wrapJSVal v = do+    ctx <- JSM ask+    liftIO $ wrapJSVal' ctx v++wrapJSVal' :: JSContextRef -> JSValueReceived -> IO JSVal+wrapJSVal' ctx (JSValueReceived n) = do+    ref <- liftIO $ newIORef n+    when (n >= 5 || n < 0) $+#ifdef JSADDLE_CHECK_WRAPJSVAL+     do lr <- takeMVar $ liveRefs ctx+        if n `S.member` lr+            then do+                putStrLn $ "JS Value Ref " <> show n <> " already wrapped"+                putMVar (liveRefs ctx) lr+            else putMVar (liveRefs ctx) =<< evaluate (S.insert n lr)+#endif+        void . mkWeakIORef ref $ do             ft <- takeMVar $ finalizerThreads ctx             t <- myThreadId             let tname = T.pack $ show t-            doSendAsyncCommand ctx $ FreeRef tname $ JSValueForSend ref+            doSendAsyncCommand ctx $ FreeRef tname $ JSValueForSend n             if tname `S.member` ft                 then putMVar (finalizerThreads ctx) ft                 else do@@ -300,5 +319,5 @@                         modifyMVar (finalizerThreads ctx) $ \s -> return (S.delete tname s, ())                         doSendAsyncCommand ctx $ FreeRefs tname                     putMVar (finalizerThreads ctx) =<< evaluate (S.insert tname ft)-    return result+    return (JSVal ref) #endif
src/Language/Javascript/JSaddle/Run/Files.hs view
@@ -244,7 +244,7 @@     \                                        (v === true           ) ? [3, \"\"] :\n\     \                                        (typeof v === \"number\") ? [-1, v.toString()] :\n\     \                                        (typeof v === \"string\") ? [-2, v]\n\-    \                                                                : [n, \"\"];\n\+    \                                                                : [-3, \"\"];\n\     \                                results.push({\"tag\": \"DeRefValResult\", \"contents\": c});\n\     \                                break;\n\     \                            case \"IsNull\":\n\
src/Language/Javascript/JSaddle/Types.hs view
@@ -141,6 +141,7 @@   , doEnableLogging        :: Bool -> IO ()   , finalizerThreads       :: MVar (Set Text)   , animationFrameHandlers :: MVar [Double -> JSM ()]+  , liveRefs               :: MVar (Set Int64) } #endif @@ -208,7 +209,7 @@ class (Applicative m, MonadIO m) => MonadJSM m where     liftJSM' :: JSM a -> m a -    default liftJSM' :: (MonadJSM m', MonadTrans t) => JSM a' -> t m' a'+    default liftJSM' :: (MonadJSM m', MonadTrans t, m ~ t m') => JSM a -> m a     liftJSM' = lift . (liftJSM' :: MonadJSM m' => JSM a -> m' a)     {-# INLINE liftJSM' #-} @@ -339,7 +340,7 @@ type STJSArray s    = SomeJSArray (STMutable s)  -- | See 'JavaScript.Object.Internal.Object'-newtype Object = Object JSVal deriving(Show, ToJSON, FromJSON)+newtype Object = Object JSVal  -- | See 'GHCJS.Nullable.Nullable' newtype Nullable a = Nullable a
src/Language/Javascript/JSaddle/Value.hs view
@@ -618,15 +618,17 @@                                        (typeof $1===\"string\")?4:\                                        (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSVal -> Int #else-deRefVal value = toJSVal value >>= N.deRefVal >>= \result -> return $-    case result of+deRefVal value = do+    v <- toJSVal value+    result <- N.deRefVal v+    return $ case result of         DeRefValResult 0    _ -> ValNull         DeRefValResult 1    _ -> ValUndefined         DeRefValResult 2    _ -> ValBool False         DeRefValResult 3    _ -> ValBool True         DeRefValResult (-1) s -> ValNumber (read (T.unpack s))         DeRefValResult (-2) s -> ValString s-        DeRefValResult ref  _ -> ValObject (Object (JSVal ref))+        DeRefValResult (-3) _ -> ValObject (Object v)         _                     -> error "Unexpected result dereferencing JSaddle value" #endif