2{-# LANGUAGE EmptyDataDecls #-}
3{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE ForeignFunctionInterface #-}
7{-# LANGUAGE FunctionalDependencies #-}
8{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE StandaloneDeriving #-}
12{-# LANGUAGE TemplateHaskell #-}
13{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeSynonymInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
17{-# LANGUAGE JavaScriptFFI #-}
19module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
24import Reflex.DynamicWriter
25import Reflex.EventWriter
26import Reflex.Host.Class
27import Reflex.PerformEvent.Base
28import Reflex.PerformEvent.Class
29import Reflex.PostBuild.Base
30import Reflex.Requester.Base
32import Language.Haskell.TH
35import qualified GHCJS.Buffer as JS
37import GHCJS.DOM.Types hiding (Text, fromJSString)
38import qualified GHCJS.DOM.Types as JS
39import qualified GHCJS.Foreign as JS
40import qualified GHCJS.Foreign.Callback as JS
41import qualified GHCJS.Foreign.Callback.Internal (Callback (..))
42import qualified GHCJS.Marshal as JS
43import qualified GHCJS.Marshal.Pure as JS
44import qualified GHCJS.Types as JS
45import qualified JavaScript.Array as JS
46import qualified JavaScript.Array.Internal (SomeJSArray (..))
47import qualified JavaScript.Object as JS
48import qualified JavaScript.Object.Internal (Object (..))
49import qualified JavaScript.TypedArray.ArrayBuffer as JSArrayBuffer
58import Graphics.UI.Gtk.WebKit.DOM.Node
59import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
60import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
61import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
62import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
63import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
64import Graphics.UI.Gtk.WebKit.WebView
68import Control.Concurrent
70import Control.Monad.Exception
71import Control.Monad.Fix
72import Control.Monad.IO.Class
73import Control.Monad.Primitive
74import Control.Monad.Reader
75import Control.Monad.Ref
76import Control.Monad.State
77import qualified Control.Monad.State.Strict as Strict
78import Control.Monad.Trans.Control
79import Data.ByteString (ByteString)
80import qualified Data.ByteString as BS
83import Data.Text (Text)
84import qualified Data.Text as T
86class Monad m => HasWebView m where
87 type WebViewPhantom m :: *
88 askWebView :: m (WebViewSingleton (WebViewPhantom m))
90instance HasWebView m => HasWebView (ReaderT r m) where
91 type WebViewPhantom (ReaderT r m) = WebViewPhantom m
92 askWebView = lift askWebView
94instance HasWebView m => HasWebView (StateT r m) where
95 type WebViewPhantom (StateT r m) = WebViewPhantom m
96 askWebView = lift askWebView
98instance HasWebView m => HasWebView (Strict.StateT r m) where
99 type WebViewPhantom (Strict.StateT r m) = WebViewPhantom m
100 askWebView = lift askWebView
102instance HasWebView m => HasWebView (PostBuildT t m) where
103 type WebViewPhantom (PostBuildT t m) = WebViewPhantom m
104 askWebView = lift askWebView
106instance (ReflexHost t, HasWebView (HostFrame t)) => HasWebView (PerformEventT t m) where
107 type WebViewPhantom (PerformEventT t m) = WebViewPhantom (HostFrame t)
108 askWebView = PerformEventT $ lift askWebView
110instance HasWebView m => HasWebView (DynamicWriterT t w m) where
111 type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
112 askWebView = lift askWebView
114instance HasWebView m => HasWebView (EventWriterT t w m) where
115 type WebViewPhantom (EventWriterT t w m) = WebViewPhantom m
116 askWebView = lift askWebView
118instance HasWebView m => HasWebView (RequesterT t request response m) where
119 type WebViewPhantom (RequesterT t request response m) = WebViewPhantom m
120 askWebView = lift askWebView
122newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
124instance PrimMonad m => PrimMonad (WithWebView x m) where
125 type PrimState (WithWebView x m) = PrimState m
126 primitive = lift . primitive
128instance MonadAdjust t m => MonadAdjust t (WithWebView x m) where
129 runWithReplace a0 a' = WithWebView $ runWithReplace (coerce a0) (coerceEvent a')
130 traverseDMapWithKeyWithAdjust f dm0 dm' = WithWebView $ traverseDMapWithKeyWithAdjust (\k v -> unWithWebView $ f k v) (coerce dm0) (coerceEvent dm')
131 traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = WithWebView $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unWithWebView $ f k v) (coerce dm0) (coerceEvent dm')
133instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
134 {-# INLINABLE newEventWithTrigger #-}
135 newEventWithTrigger = lift . newEventWithTrigger
136 {-# INLINABLE newFanEventWithTrigger #-}
137 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
139instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView x m) where
140 {-# INLINABLE subscribeEvent #-}
141 subscribeEvent = lift . subscribeEvent
143instance MonadReflexHost t m => MonadReflexHost t (WithWebView x m) where
144 type ReadPhase (WithWebView x m) = ReadPhase m
145 {-# INLINABLE fireEventsAndRead #-}
146 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
147 {-# INLINABLE runHostFrame #-}
148 runHostFrame = lift . runHostFrame
150instance MonadSample t m => MonadSample t (WithWebView x m) where
151 {-# INLINABLE sample #-}
152 sample = lift . sample
154instance MonadHold t m => MonadHold t (WithWebView x m) where
155 {-# INLINABLE hold #-}
156 hold v0 = lift . hold v0
157 {-# INLINABLE holdDyn #-}
158 holdDyn v0 = lift . holdDyn v0
159 {-# INLINABLE holdIncremental #-}
160 holdIncremental v0 = lift . holdIncremental v0
162instance MonadTransControl (WithWebView x) where
163 type StT (WithWebView x) a = StT (ReaderT (WebViewSingleton x)) a
164 {-# INLINABLE liftWith #-}
165 liftWith = defaultLiftWith WithWebView unWithWebView
166 {-# INLINABLE restoreT #-}
167 restoreT = defaultRestoreT WithWebView
169instance PerformEvent t m => PerformEvent t (WithWebView x m) where
170 type Performable (WithWebView x m) = WithWebView x (Performable m) --TODO: Can we eliminate this wrapper?
171 {-# INLINABLE performEvent_ #-}
172 performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
173 {-# INLINABLE performEvent #-}
174 performEvent e = liftWith $ \run -> performEvent $ fmap run e
176instance ExhaustiblePerformEvent t m => ExhaustiblePerformEvent t (WithWebView x m) where
177 withPerformEventExhausted a = liftWith $ \run -> withPerformEventExhausted $ run a
179runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
180runWithWebView = runReaderT . unWithWebView
182instance (Monad m) => HasWebView (WithWebView x m) where
183 type WebViewPhantom (WithWebView x m) = x
184 askWebView = WithWebView ask
186instance MonadRef m => MonadRef (WithWebView x m) where
187 type Ref (WithWebView x m) = Ref m
188 newRef = lift . newRef
189 readRef = lift . readRef
190 writeRef r = lift . writeRef r
192instance MonadAtomicRef m => MonadAtomicRef (WithWebView x m) where
193 atomicModifyRef r = lift . atomicModifyRef r
195withWebViewSingleton :: WebView -> (forall x. WebViewSingleton x -> r) -> r
196withWebViewSingleton wv f = f $ WebViewSingleton wv
198-- | Warning: `withWebViewSingletonMono` does not provide the same guarantees that `withWebViewSingleton` does.
199withWebViewSingletonMono :: WebView -> (WebViewSingleton () -> r) -> r
200withWebViewSingletonMono wv f = f $ WebViewSingleton wv
202-- | A singleton type for a given WebView; we use this to statically guarantee that different WebViews (and thus different javscript contexts) don't get mixed up
203newtype WebViewSingleton x = WebViewSingleton { unWebViewSingleton :: WebView }
206type JSFFI_Internal = JS.MutableJSArray -> IO JS.JSVal
207newtype JSFFI = JSFFI JSFFI_Internal
209newtype JSFFI = JSFFI String
212newtype JSFun x = JSFun { unJSFun :: JSRef x }
214instance ToJS x (JSFun x) where
215 withJS (JSFun r) f = f r
217instance FromJS x (JSFun x) where
218 fromJS = return . JSFun
220class IsJSContext x where
223class (Monad m, MonadIO (JSM m), MonadFix (JSM m), MonadJS x (JSM m)) => HasJS x m | m -> x where
225 liftJS :: JSM m a -> m a
227instance HasJS x m => HasJS x (ReaderT r m) where
228 type JSM (ReaderT r m) = JSM m
229 liftJS = lift . liftJS
231instance (HasJS x m, ReflexHost t) => HasJS x (PostBuildT t m) where
232 type JSM (PostBuildT t m) = JSM m
233 liftJS = lift . liftJS
235instance (HasJS x (HostFrame t), ReflexHost t) => HasJS x (PerformEventT t m) where
236 type JSM (PerformEventT t m) = JSM (HostFrame t)
237 liftJS = PerformEventT . lift . liftJS
239instance HasJS x m => HasJS x (DynamicWriterT t w m) where
240 type JSM (DynamicWriterT t w m) = JSM m
241 liftJS = lift . liftJS
243instance HasJS x m => HasJS x (RequesterT t request response m) where
244 type JSM (RequesterT t request response m) = JSM m
245 liftJS = lift . liftJS
247-- | A Monad that is capable of executing JavaScript
248class Monad m => MonadJS x m | m -> x where
249 runJS :: JSFFI -> [JSRef x] -> m (JSRef x)
250 forkJS :: m () -> m ThreadId
251 mkJSUndefined :: m (JSRef x)
252 isJSNull :: JSRef x -> m Bool
253 isJSUndefined :: JSRef x -> m Bool
254 fromJSBool :: JSRef x -> m Bool
255 fromJSString :: JSRef x -> m String
256 fromJSArray :: JSRef x -> m [JSRef x]
257 fromJSUint8Array :: JSRef x -> m ByteString
258 fromJSNumber :: JSRef x -> m Double
259 withJSBool :: Bool -> (JSRef x -> m r) -> m r
260 withJSString :: String -> (JSRef x -> m r) -> m r
261 withJSNumber :: Double -> (JSRef x -> m r) -> m r
262 withJSArray :: [JSRef x] -> (JSRef x -> m r) -> m r
263 withJSUint8Array :: ByteString -> (JSUint8Array x -> m r) -> m r
264 -- | Create a JSFun with zero arguments; should be equilvant to `syncCallback AlwaysRetain True` in GHCJS
265 mkJSFun :: ([JSRef x] -> m (JSRef x)) -> m (JSFun x) --TODO: Support 'this', exceptions
266 freeJSFun :: JSFun x -> m ()
267 setJSProp :: String -> JSRef x -> JSRef x -> m ()
268 getJSProp :: String -> JSRef x -> m (JSRef x)
269 withJSNode :: Node -> (JSRef x -> m r) -> m r
275instance MonadIO m => HasJS JSCtx_IO (WithWebView x m) where
276 type JSM (WithWebView x m) = IO
279instance IsJSContext JSCtx_IO where
280 newtype JSRef JSCtx_IO = JSRef_IO { unJSRef_IO :: JS.JSVal }
282instance MonadJS JSCtx_IO IO where
283 runJS (JSFFI f) l = fmap JSRef_IO . f =<< JS.fromListIO (coerce l)
285 mkJSUndefined = return $ JSRef_IO JS.jsUndefined
286 isJSNull (JSRef_IO r) = return $ JS.isNull r
287 isJSUndefined (JSRef_IO r) = return $ JS.isUndefined r
288 fromJSBool (JSRef_IO r) = return $ JS.fromJSBool r
289 fromJSString (JSRef_IO r) = return $ JS.fromJSString $ JS.pFromJSVal r
290 fromJSArray (JSRef_IO r) = fmap coerce $ JS.toListIO $ coerce r
291 fromJSUint8Array (JSRef_IO r) = fmap (JS.toByteString 0 Nothing . JS.createFromArrayBuffer) $ JSArrayBuffer.unsafeFreeze $ JS.pFromJSVal r --TODO: Assert that this is immutable
292 fromJSNumber (JSRef_IO r) = do
293 Just n <- JS.fromJSVal r
295 withJSBool b f = f $ JSRef_IO $ JS.toJSBool b
296 withJSString s f = f $ JSRef_IO $ JS.pToJSVal $ JS.toJSString s
297 withJSNumber n f = do
301 r <- JS.fromListIO $ coerce l
302 f $ JSRef_IO $ coerce r
303 withJSUint8Array payload f = BS.useAsCString payload $ \cStr -> do
304 ba <- extractByteArray cStr $ BS.length payload
305 f $ JSUint8Array $ JSRef_IO ba
307 cb <- JS.syncCallback1' $ \args -> do
308 l <- JS.toListIO $ coerce args
309 JSRef_IO result <- f $ coerce l
311 fmap (JSFun . JSRef_IO) $ funWithArguments $ coerce cb
312 freeJSFun (JSFun (JSRef_IO r)) = JS.releaseCallback $ coerce r
313 setJSProp s (JSRef_IO v) (JSRef_IO o) = JS.setProp (JS.toJSString s) v $ coerce o
314 getJSProp s (JSRef_IO o) = do
315 r <- JS.getProp (JS.toJSString s) $ coerce o
317 withJSNode n f = f $ JSRef_IO $ unNode n
319foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)" extractByteArray :: Ptr CChar -> Int -> IO JS.JSVal
321foreign import javascript unsafe "function(){ return $1(arguments); }" funWithArguments :: JS.Callback (JS.MutableJSArray -> IO a) -> IO JS.JSVal
325-- | Make a JS string available that is not wrapped up as a JSValueRef
326withJSStringRaw :: MonadAsyncException m => String -> (JSStringRef -> m r) -> m r
327withJSStringRaw str a = do
328 bracket (liftIO $ jsstringcreatewithutf8cstring str) (liftIO . jsstringrelease) $ \strRef -> do
331fromJSNumberRaw :: (MonadIO m, HasJSContext m) => JSValueRef -> m Double
332fromJSNumberRaw val = do
333 jsContext <- askJSContext
334 liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
337data JSCtx_JavaScriptCore x
339instance IsJSContext (JSCtx_JavaScriptCore x) where
340 newtype JSRef (JSCtx_JavaScriptCore x) = JSRef_JavaScriptCore { unJSRef_JavaScriptCore :: JSValueRef }
342instance MonadIO m => HasJS (JSCtx_JavaScriptCore x) (WithWebView x m) where
343 type JSM (WithWebView x m) = WithWebView x IO
346 liftIO $ runWithWebView a wv
348newtype WithJSContext x m a = WithJSContext { unWithJSContext :: ReaderT JSContextRef m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
350runWithJSContext :: WithJSContext x m a -> JSContextRef -> m a
351runWithJSContext = runReaderT . unWithJSContext
353class Monad m => HasJSContext m where
354 askJSContext :: m JSContextRef
356instance MonadIO m => HasJSContext (WithWebView x m) where
359 liftIO $ webFrameGetGlobalContext =<< webViewGetMainFrame (unWebViewSingleton wv)
361instance Monad m => HasJSContext (WithJSContext x m) where
362 askJSContext = WithJSContext ask
364lowerWithJSContext :: (HasJSContext m, MonadIO m) => WithJSContext x IO a -> m a
365lowerWithJSContext a = do
367 liftIO $ runWithJSContext a c
369liftWithWebViewThroughWithJSContext :: (HasJSContext m, HasWebView m, MonadIO m, MonadTrans t, Monad m1)
370 => ((t1 -> t m1 a) -> WithJSContext x IO b)
371 -> (t1 -> WithWebView (WebViewPhantom m) m1 a)
373liftWithWebViewThroughWithJSContext f a = do
375 lowerWithJSContext $ f $ \b' -> lift $ runWithWebView (a b') wv
377instance MonadJS (JSCtx_JavaScriptCore x) (WithWebView x IO) where
380 liftIO $ forkIO $ runWithWebView a wv
383 lowerWithJSContext $ mkJSFun $ \args -> lift $ runWithWebView (a args) wv
384 runJS expr args = lowerWithJSContext $ runJS expr args
385 mkJSUndefined = lowerWithJSContext mkJSUndefined
386 isJSNull = lowerWithJSContext . isJSNull
387 isJSUndefined = lowerWithJSContext . isJSUndefined
388 fromJSBool = lowerWithJSContext . fromJSBool
389 fromJSString = lowerWithJSContext . fromJSString
390 fromJSArray = lowerWithJSContext . fromJSArray
391 fromJSUint8Array = lowerWithJSContext . fromJSUint8Array
392 fromJSNumber = lowerWithJSContext . fromJSNumber
393 freeJSFun = lowerWithJSContext . freeJSFun
394 withJSBool = liftWithWebViewThroughWithJSContext . withJSBool
395 withJSString = liftWithWebViewThroughWithJSContext . withJSString
396 withJSNumber = liftWithWebViewThroughWithJSContext . withJSNumber
397 withJSArray = liftWithWebViewThroughWithJSContext . withJSArray
398 withJSUint8Array = liftWithWebViewThroughWithJSContext . withJSUint8Array
399 withJSNode = liftWithWebViewThroughWithJSContext . withJSNode
400 setJSProp propName valRef objRef = lowerWithJSContext $ setJSProp propName valRef objRef
401 getJSProp propName objRef = lowerWithJSContext $ getJSProp propName objRef
403instance MonadJS (JSCtx_JavaScriptCore x) (WithJSContext x IO) where
404 runJS (JSFFI body) args = do
405 jsContext <- askJSContext
406 withJSArray args $ \(JSRef_JavaScriptCore this) -> liftIO $ do
407 result <- bracket (jsstringcreatewithutf8cstring body) jsstringrelease $ \script -> jsevaluatescript jsContext script this nullPtr 1 nullPtr
408 return $ JSRef_JavaScriptCore result --TODO: Protect and unprotect result
411 liftIO $ forkIO $ runWithJSContext a c
413 jsContext <- askJSContext
414 fmap JSRef_JavaScriptCore $ liftIO $ jsvaluemakeundefined jsContext
415 isJSNull (JSRef_JavaScriptCore r) = do
416 jsContext <- askJSContext
417 liftIO $ jsvalueisnull jsContext r
418 isJSUndefined (JSRef_JavaScriptCore r) = do
419 jsContext <- askJSContext
420 liftIO $ jsvalueisundefined jsContext r
421 fromJSBool (JSRef_JavaScriptCore r) = do
422 jsContext <- askJSContext
423 liftIO $ jsvaluetoboolean jsContext r
424 fromJSString (JSRef_JavaScriptCore r) = do
425 jsContext <- askJSContext
427 s <- jsvaluetostringcopy jsContext r nullPtr --TODO: Deal with exceptions
428 l <- jsstringgetmaximumutf8cstringsize s
429 allocaBytes (fromIntegral l) $ \ps -> do
430 _ <- jsstringgetutf8cstring'_ s ps (fromIntegral l)
433 jsContext <- askJSContext
434 valRef <- liftIO $ jsvaluemakeboolean jsContext b
435 a $ JSRef_JavaScriptCore valRef
436 withJSString str a = do
437 jsContext <- askJSContext
438 withJSStringRaw str $ \strRef -> do
439 valRef <- liftIO $ jsvaluemakestring jsContext strRef
440 a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
441 withJSNumber n a = do
442 jsContext <- askJSContext
443 valRef <- liftIO $ jsvaluemakenumber jsContext n
444 a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
445 withJSArray elems a = do
446 jsContext <- askJSContext
447 let numElems = length elems
448 bracket (liftIO $ mallocArray numElems) (liftIO . free) $ \elemsArr -> do
449 liftIO $ pokeArray elemsArr $ coerce elems
450 bracket (liftIO $ jsobjectmakearray jsContext (fromIntegral numElems) elemsArr nullPtr) (const $ return ()) $ \arrRef -> do --TODO: Do we need to protect/unprotect the array object?
451 a $ JSRef_JavaScriptCore arrRef --TODO: Protect/unprotect valRef
452 --TODO: When supported by webkitgtk3-javascriptcore, go directly from C string to Uint8Array without creating each item individually
453 withJSUint8Array payload f = withJSArrayFromList (BS.unpack payload) $ \x -> do
454 payloadRef <- runJS (JSFFI "new Uint8Array(this[0])") [x]
455 f $ JSUint8Array payloadRef
456 fromJSArray (JSRef_JavaScriptCore a) = do
457 jsContext <- askJSContext
458 lenRef <- withJSStringRaw "length" $ \lengthStr -> do
459 liftIO $ jsobjectgetproperty jsContext a lengthStr nullPtr --TODO: Exceptions
460 lenDouble <- fromJSNumberRaw lenRef
461 let len = round lenDouble
462 liftIO $ forM [0..len-1] $ \i -> do
463 JSRef_JavaScriptCore <$> jsobjectgetpropertyatindex jsContext a i nullPtr --TODO: Exceptions
464 fromJSUint8Array a = do
465 vals <- fromJSArray a
466 doubles <- mapM fromJSNumber vals
467 return $ BS.pack $ map round doubles
468 fromJSNumber (JSRef_JavaScriptCore val) = do
469 jsContext <- askJSContext
470 liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
472 jsContext <- askJSContext
473 cb <- liftIO $ mkJSObjectCallAsFunctionCallback $ \_ _ _ argc argv _ -> do --TODO: Use the exception pointer to handle haskell exceptions
474 args <- forM [0..fromIntegral (argc-1)] $ \n -> do
475 x <- peekElemOff argv n
476 jsvalueprotect jsContext x
477 return $ JSRef_JavaScriptCore x --TODO: Unprotect eventually
478 unJSRef_JavaScriptCore <$> runWithJSContext (a args) jsContext
479 cbRef <- liftIO $ jsobjectmakefunctionwithcallback jsContext nullPtr cb
480 liftIO $ jsvalueprotect jsContext cbRef
481 return $ JSFun $ JSRef_JavaScriptCore cbRef
482 freeJSFun (JSFun (JSRef_JavaScriptCore f)) = do
483 jsContext <- askJSContext
484 liftIO $ jsvalueunprotect jsContext f
485 setJSProp propName (JSRef_JavaScriptCore valRef) (JSRef_JavaScriptCore objRef) = do
486 withJSStringRaw propName $ \propNameRaw -> do
487 jsContext <- askJSContext
488 liftIO $ jsobjectsetproperty jsContext objRef propNameRaw valRef 0 nullPtr --TODO: property attribute, exceptions
489 getJSProp propName (JSRef_JavaScriptCore objRef) = do
490 withJSStringRaw propName $ \propNameRaw -> do
491 jsContext <- askJSContext
492 liftIO $ JSRef_JavaScriptCore <$> jsobjectgetproperty jsContext objRef propNameRaw nullPtr --TODO: property attribute, exceptions
493 withJSNode = error "withJSNode is only supported in ghcjs"
497class FromJS x a where
498 fromJS :: MonadJS x m => JSRef x -> m a
500instance FromJS x () where
501 fromJS _ = return () --TODO: Should this do some kind of checking for the js value?
503instance FromJS x Bool where
506instance ToJS x Bool where
509instance FromJS x String where
510 fromJS = fromJSString
512instance FromJS x Text where
513 fromJS s = T.pack <$> fromJSString s
515instance FromJS x a => FromJS x (Maybe a) where
518 if n then return Nothing else Just <$> fromJS x
521 withJS :: MonadJS x m => a -> (JSRef x -> m r) -> m r
523instance ToJS x (JSRef x) where
526instance FromJS x (JSRef x) where
529instance ToJS x String where
530 withJS = withJSString
532instance ToJS x Text where
533 withJS = withJSString . T.unpack
535newtype JSArray a = JSArray { unJSArray :: [a] }
537instance ToJS x a => ToJS x (JSArray a) where
538 withJS = withJSArrayFromList . unJSArray
540instance FromJS x a => FromJS x (JSArray a) where
541 fromJS = fmap JSArray . mapM fromJS <=< fromJSArray
543withJSArrayFromList :: (ToJS x a, MonadJS x m) => [a] -> (JSRef x -> m r) -> m r
544withJSArrayFromList as f = go as []
545 where go [] jsRefs = withJSArray (reverse jsRefs) f
546 go (h:t) jsRefs = withJS h $ \hRef -> go t (hRef : jsRefs)
548newtype JSUint8Array x = JSUint8Array { unJSUint8Array :: JSRef x }
550instance ToJS x (JSUint8Array x) where
551 withJS (JSUint8Array r) = ($ r)
553instance FromJS x (JSUint8Array x) where
554 fromJS = return . JSUint8Array
556instance ToJS x Word8 where
557 withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
559instance ToJS x Int where
560 withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
562instance FromJS x Int where
563 fromJS = fmap round . fromJSNumber --TODO: Check things; throw exceptions
565instance ToJS x Double where
566 withJS = withJSNumber
568instance FromJS x Double where
569 fromJS = fromJSNumber
571instance ToJS x Node where
575importJS :: Safety -> String -> String -> Q Type -> Q [Dec]
576importJS safety body name qt = do
578 let (argTypes, _) = parseType t
579 argNames <- forM argTypes $ \_ -> do
581 argRef <- newName "argRef"
583 (jsffiDecs, jsffiExp) <- mkJSFFI safety body
584 let go [] = [| runJS $(return jsffiExp) $(listE $ map (varE . snd) argNames) >>= fromJS
586 go ((arg, argRef) : args) = [| withJS $(varE arg) $ $(lamE [varP argRef] $ go args) |]
587 e <- lamE (map (varP. fst) argNames) $ go argNames
589 return $ jsffiDecs ++
591 , ValD (VarP n) (NormalB e) []
594mkJSFFI :: Safety -> String -> Q ([Dec], Exp)
596mkJSFFI safety body = do
597 -- n <- newName "jsffi" --TODO: Should use newName, but that doesn't seem to work with ghcjs
599 n <- newName $ "jsffi_" <> zEncodeString (loc_package l <> ":" <> loc_module l) <> "_" <> show (abs (hash (show safety, body)))
600 t <- [t| JSFFI_Internal |]
601 let wrappedBody = "(function(){ return (" <> body <> "); }).apply($1)"
602 let decs = [ForeignD $ ImportF JavaScript safety wrappedBody n t]
603 e <- [| JSFFI $(varE n) |]
607 e <- [| JSFFI body |]
611parseType :: Type -> ([Type], Type)
612parseType (ForallT _ [AppT (AppT (ConT monadJs) (VarT _)) (VarT m)] funType)
613 | monadJs == ''MonadJS = go funType
614 where go t = case t of
615 AppT (AppT ArrowT arg) t' ->
616 let (args, result) = go t'
617 in (arg : args, result)
618 AppT (VarT m') result
619 | m' == m -> ([], result)
620 _ -> error $ "parseType: can't parse type " <> show t
621parseType t = error $ "parseType: can't parse type " <> show t
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list