ghcjs-base-0.8.0.4: GHCJS/Foreign/Export.hs
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE JavaScriptFFI #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE EmptyDataDecls #-}
{- |
Dynamically export Haskell values to JavaScript
-}
module GHCJS.Foreign.Export
( Export
, export
, withExport
, derefExport
, releaseExport
) where
import Control.Exception (bracket)
import GHC.Exts (Any)
import GHC.Fingerprint
import Data.Typeable
import Data.Word
import Unsafe.Coerce
import qualified GHC.Exts as Exts
import GHC.JS.Prim
import GHCJS.Types
newtype Export a = Export JSVal
instance IsJSVal (Export a)
{- |
Export any Haskell value to a JavaScript reference without evaluating it.
The JavaScript reference can be passed to foreign code and used to retrieve
the value later.
The data referenced by the value will be kept in memory until you call
'releaseExport', even if no foreign code references the export anymore.
-}
export :: Typeable a => a -> IO (Export a)
export x = js_export w1 w2 (unsafeCoerce x)
where
Fingerprint w1 w2 = typeRepFingerprint (typeOf x)
{- |
Export the value and run the action. The value is only exported for the
duration of the action. Dereferencing it after the 'withExport' call
has returned will always return 'Nothing'.
-}
-- fixme is this safe with nested exports?
withExport :: Typeable a => a -> (Export a -> IO b) -> IO b
withExport x m = bracket (export x) releaseExport m
{- |
Retrieve the Haskell value from an export. Returns 'Nothing' if the
type does not match or the export has already been released.
-}
derefExport :: forall a. Typeable a => Export a -> IO (Maybe a)
derefExport e = do
let Fingerprint w1 w2 = typeRepFingerprint (typeOf (undefined :: a))
r <- js_derefExport w1 w2 e
if isNull r
then return Nothing
else Just . unsafeCoerce <$> js_toHeapObject r
{- |
Release all memory associated with the export. Subsequent calls to
'derefExport' will return 'Nothing'
-}
releaseExport :: Export a -> IO ()
releaseExport e = js_releaseExport e
-- ----------------------------------------------------------------------------
foreign import javascript unsafe
"h$exportValue"
js_export :: Word64 -> Word64 -> Any -> IO (Export a)
foreign import javascript unsafe
"h$derefExport"
js_derefExport :: Word64 -> Word64 -> Export a -> IO JSVal
foreign import javascript unsafe
"((x) => { return x; })" js_toHeapObject :: JSVal -> IO Any
foreign import javascript unsafe
"h$releaseExport"
js_releaseExport :: Export a -> IO ()