on master help sign in
Many changes and bug fixes

Tries to bring the type names more in line with ghcjs-base (so JSVal instead of JSValueRef). In the native version JSVal is a ForeignPtr (where as JSValueRef is a Ptr). Among the bug fixes are some important reference counting fixes for the native version. Referenced values are now protected from collection by JavaScriptCore (the protection ends when a Haskell ForeignPtr finalizer runs).

commit4555d2e14f3771ecf8344fadb57f980405789050 authorHamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> dateFri 12 Feb 2016, 00:38 (11 years ago) parent0dcf7e1
14 files changed+821−432
jsaddle.cabal+77−50 src/Language/Javascript/JSaddle.hs+2 src/Language/Javascript/JSaddle/Arguments.hs+34−28 src/Language/Javascript/JSaddle/Classes.hs+18−18 src/Language/Javascript/JSaddle/Evaluate.hs+18−18 src/Language/Javascript/JSaddle/Exception.hs−1 src/Language/Javascript/JSaddle/Monad.hs+42−18 src/Language/Javascript/JSaddle/Native.hs+97 src/Language/Javascript/JSaddle/Object.hs+173−90 src/Language/Javascript/JSaddle/Properties.hs+37−40 src/Language/Javascript/JSaddle/String.hs+19−22 src/Language/Javascript/JSaddle/Types.hs+30−12 src/Language/Javascript/JSaddle/Value.hs+252−121 tests/TestJSaddle.hs+22−14
jsaddle.cabal+77−50
@@ -1,87 +1,114 @@
11name: jsaddle
22version: 0.3.0.0
33cabal-version: >=1.10
4+build-type: Simple
45license: MIT
56license-file: LICENSE
67maintainer: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
78synopsis: High level interface for webkit-javascriptcore
8−description: This package provides an EDSL for calling JavaScript code using
9− the JavaScriptCore engine and low level Haskell bindings
10− in the webkit-javascriptcore library <https://github.com/ghcjs/webkit-javascriptcore>.
11−author: Hamish Mackenzie
12−data-dir: ""
9+description:
10+ This package provides an EDSL for calling JavaScript code using
11+ the JavaScriptCore engine and low level Haskell bindings
12+ in the webkit-javascriptcore library <https://github.com/ghcjs/webkit-javascriptcore>.
1313category: Web, Javascript
14−build-type: Simple
14+author: Hamish Mackenzie
1515
16−Source-Repository head
17− type: git
18− location: https://github.com/ghcjs/jsaddle
16+source-repository head
17+ type: git
18+ location: https://github.com/ghcjs/jsaddle
1919
2020flag gtk3
21− Description: Use GTK3 rather than GTK2
22− Default: True
21+ description:
22+ Use GTK3 rather than GTK2
2323
2424flag jsffi
25− description: Use JavaScriptFFI extension for GHCJS JavaScript output
26− default: True
25+ description:
26+ Use JavaScriptFFI extension for GHCJS JavaScript output
2727
2828flag webkit
29− description: Use WebKitGtk and JavaScriptCore for GHCJS output
29+ description:
30+ Use WebKitGtk and JavaScriptCore for GHCJS output
3031 default: False
3132
3233library
33− if impl(ghcjs) && flag(jsffi)
34+
35+ if (impl(ghcjs -any) && flag(jsffi))
36+ build-depends:
37+ ghcjs-base -any,
38+ ghcjs-prim -any
3439 cpp-options: -DUSE_JAVASCRIPTFFI
35− build-depends: ghcjs-base,
36− ghcjs-prim
3740
38− if !impl(ghcjs) || flag(webkit)
39− cpp-options: -DUSE_WEBKIT
41+ if (!impl(ghcjs -any) || flag(webkit))
42+
4043 if flag(gtk3)
41− build-depends: webkitgtk3 >=0.13.0.0 && <0.15,
42− webkitgtk3-javascriptcore >=0.13.0.0 && <0.14
44+ build-depends:
45+ gtk3 >=0.14.2 && <0.15,
46+ webkitgtk3 >=0.13.0.0 && <0.15,
47+ webkitgtk3-javascriptcore >=0.13.0.0 && <0.14
4348 else
44− build-depends: webkit >=0.13.0.0 && <0.15,
45− webkit-javascriptcore >=0.13.0.0 && <0.14
46−
47− build-depends: template-haskell -any, base <5, lens >=3.8.5 && <4.14,
48− text >=0.11.2.3 && <1.3, transformers >=0.3.0.0 && <0.5
49−
50− exposed-modules: Language.Javascript.JSaddle
51− Language.Javascript.JSaddle.Arguments Language.Javascript.JSaddle.Classes
52− Language.Javascript.JSaddle.Evaluate Language.Javascript.JSaddle.Exception
53− Language.Javascript.JSaddle.Monad
54− Language.Javascript.JSaddle.Object Language.Javascript.JSaddle.Properties
55− Language.Javascript.JSaddle.String Language.Javascript.JSaddle.Value
56− Language.Javascript.JSaddle.Types
57− exposed: True
58− buildable: True
49+ build-depends:
50+ gtk >=0.14.2 && <0.15,
51+ webkit >=0.13.0.0 && <0.15,
52+ webkit-javascriptcore >=0.13.0.0 && <0.14
53+ cpp-options: -DUSE_WEBKIT
54+ exposed-modules:
55+ Language.Javascript.JSaddle
56+ Language.Javascript.JSaddle.Arguments
57+ Language.Javascript.JSaddle.Classes
58+ Language.Javascript.JSaddle.Evaluate
59+ Language.Javascript.JSaddle.Exception
60+ Language.Javascript.JSaddle.Monad
61+ Language.Javascript.JSaddle.Native
62+ Language.Javascript.JSaddle.Object
63+ Language.Javascript.JSaddle.Properties
64+ Language.Javascript.JSaddle.String
65+ Language.Javascript.JSaddle.Value
66+ Language.Javascript.JSaddle.Types
67+ build-depends:
68+ template-haskell -any,
69+ base <5,
70+ lens >=3.8.5 && <4.14,
71+ text >=0.11.2.3 && <1.3,
72+ transformers >=0.3.0.0 && <0.5
5973 default-language: Haskell2010
6074 hs-source-dirs: src
6175 ghc-options: -ferror-spans
6276
6377test-suite test-tool
64− if !impl(ghcjs) || flag(webkit)
65− buildable: True
66− else
78+
79+ if (impl(ghcjs -any) && !flag(webkit))
6780 buildable: False
6881
69− if impl(ghcjs) && flag(jsffi)
82+ if (impl(ghcjs -any) && flag(jsffi))
83+ build-depends:
84+ ghcjs-base -any,
85+ ghcjs-prim -any
7086 cpp-options: -DUSE_JAVASCRIPTFFI
71− build-depends: ghcjs-base,
72− ghcjs-prim
7387
74− if !impl(ghcjs) || flag(webkit)
75− cpp-options: -DUSE_WEBKIT
76− build-depends: glib
88+ if (!impl(ghcjs -any) || flag(webkit))
89+
7790 if flag(gtk3)
78− build-depends: gtk3, webkitgtk3, webkitgtk3-javascriptcore
91+ build-depends:
92+ gtk3 -any,
93+ webkitgtk3 -any,
94+ webkitgtk3-javascriptcore -any
7995 else
80− build-depends: gtk, webkit, webkit-javascriptcore
81−
82− build-depends: template-haskell, base, lens, text,
83− transformers, hslogger, jsaddle
96+ build-depends:
97+ gtk -any,
98+ webkit -any,
99+ webkit-javascriptcore -any
100+ build-depends:
101+ glib -any
102+ cpp-options: -DUSE_WEBKIT
84103 type: exitcode-stdio-1.0
85104 main-is: TestJSaddle.hs
105+ build-depends:
106+ template-haskell -any,
107+ base -any,
108+ lens -any,
109+ text -any,
110+ transformers -any,
111+ hslogger -any,
112+ jsaddle -any
86113 default-language: Haskell2010
87114 hs-source-dirs: tests
src/Language/Javascript/JSaddle.hs+2
@@ -52,6 +52,8 @@ module Language.Javascript.JSaddle (
5252 module JSaddle
5353) where
5454
55+import Language.Javascript.JSaddle.Types as JSaddle
56+import Language.Javascript.JSaddle.Classes as JSaddle
5557import Language.Javascript.JSaddle.Monad as JSaddle
5658import Language.Javascript.JSaddle.Exception as JSaddle
5759import Language.Javascript.JSaddle.Value as JSaddle
src/Language/Javascript/JSaddle/Arguments.hs+34−28
@@ -17,60 +17,66 @@ module Language.Javascript.JSaddle.Arguments (
1717) where
1818
1919import Language.Javascript.JSaddle.Classes
20− (MakeVal(..), MakeArgs(..))
20+ (ToJSVal(..))
2121import Language.Javascript.JSaddle.Monad (JSM)
22+import Language.Javascript.JSaddle.Types (JSVal)
23+
24+-- | Anything that can be used to make a list of JavaScript value
25+-- references for use as function arguments
26+class MakeArgs this where
27+ makeArgs :: this -> JSM [JSVal]
2228
2329instance MakeArgs arg => MakeArgs (JSM arg) where
2430 makeArgs arg = arg >>= makeArgs
2531 {-# INLINE makeArgs #-}
2632
27−instance MakeVal arg => MakeArgs [arg] where
28− makeArgs = mapM makeVal
33+instance ToJSVal arg => MakeArgs [arg] where
34+ makeArgs = mapM toJSVal
2935 {-# INLINE makeArgs #-}
3036
31−instance (MakeVal arg1, MakeVal arg2) => MakeArgs (arg1, arg2) where
37+instance (ToJSVal arg1, ToJSVal arg2) => MakeArgs (arg1, arg2) where
3238 makeArgs (arg1, arg2) = do
33− rarg1 <- makeVal arg1
34− rarg2 <- makeVal arg2
39+ rarg1 <- toJSVal arg1
40+ rarg2 <- toJSVal arg2
3541 return [rarg1, rarg2]
3642 {-# INLINE makeArgs #-}
3743
38−instance (MakeVal arg1, MakeVal arg2, MakeVal arg3) => MakeArgs (arg1, arg2, arg3) where
44+instance (ToJSVal arg1, ToJSVal arg2, ToJSVal arg3) => MakeArgs (arg1, arg2, arg3) where
3945 makeArgs (arg1, arg2, arg3) = do
40− rarg1 <- makeVal arg1
41− rarg2 <- makeVal arg2
42− rarg3 <- makeVal arg3
46+ rarg1 <- toJSVal arg1
47+ rarg2 <- toJSVal arg2
48+ rarg3 <- toJSVal arg3
4349 return [rarg1, rarg2, rarg3]
4450 {-# INLINE makeArgs #-}
4551
46−instance (MakeVal arg1, MakeVal arg2, MakeVal arg3, MakeVal arg4) => MakeArgs (arg1, arg2, arg3, arg4) where
52+instance (ToJSVal arg1, ToJSVal arg2, ToJSVal arg3, ToJSVal arg4) => MakeArgs (arg1, arg2, arg3, arg4) where
4753 makeArgs (arg1, arg2, arg3, arg4) = do
48− rarg1 <- makeVal arg1
49− rarg2 <- makeVal arg2
50− rarg3 <- makeVal arg3
51− rarg4 <- makeVal arg4
54+ rarg1 <- toJSVal arg1
55+ rarg2 <- toJSVal arg2
56+ rarg3 <- toJSVal arg3
57+ rarg4 <- toJSVal arg4
5258 return [rarg1, rarg2, rarg3, rarg4]
5359 {-# INLINE makeArgs #-}
5460
55−instance (MakeVal arg1, MakeVal arg2, MakeVal arg3, MakeVal arg4, MakeVal arg5) => MakeArgs (arg1, arg2, arg3, arg4, arg5) where
61+instance (ToJSVal arg1, ToJSVal arg2, ToJSVal arg3, ToJSVal arg4, ToJSVal arg5) => MakeArgs (arg1, arg2, arg3, arg4, arg5) where
5662 makeArgs (arg1, arg2, arg3, arg4, arg5) = do
57− rarg1 <- makeVal arg1
58− rarg2 <- makeVal arg2
59− rarg3 <- makeVal arg3
60− rarg4 <- makeVal arg4
61− rarg5 <- makeVal arg5
63+ rarg1 <- toJSVal arg1
64+ rarg2 <- toJSVal arg2
65+ rarg3 <- toJSVal arg3
66+ rarg4 <- toJSVal arg4
67+ rarg5 <- toJSVal arg5
6268 return [rarg1, rarg2, rarg3, rarg4, rarg5]
6369 {-# INLINE makeArgs #-}
6470
6571
66−instance (MakeVal arg1, MakeVal arg2, MakeVal arg3, MakeVal arg4, MakeVal arg5, MakeVal arg6) => MakeArgs (arg1, arg2, arg3, arg4, arg5, arg6) where
72+instance (ToJSVal arg1, ToJSVal arg2, ToJSVal arg3, ToJSVal arg4, ToJSVal arg5, ToJSVal arg6) => MakeArgs (arg1, arg2, arg3, arg4, arg5, arg6) where
6773 makeArgs (arg1, arg2, arg3, arg4, arg5, arg6) = do
68− rarg1 <- makeVal arg1
69− rarg2 <- makeVal arg2
70− rarg3 <- makeVal arg3
71− rarg4 <- makeVal arg4
72− rarg5 <- makeVal arg5
73− rarg6 <- makeVal arg6
74+ rarg1 <- toJSVal arg1
75+ rarg2 <- toJSVal arg2
76+ rarg3 <- toJSVal arg3
77+ rarg4 <- toJSVal arg4
78+ rarg5 <- toJSVal arg5
79+ rarg6 <- toJSVal arg6
7480 return [rarg1, rarg2, rarg3, rarg4, rarg5, rarg6]
7581 {-# INLINE makeArgs #-}
7682
src/Language/Javascript/JSaddle/Classes.hs+18−18
@@ -1,3 +1,5 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE TypeSynonymInstances #-}
13-----------------------------------------------------------------------------
24--
35-- Module : Language.Javascript.JSaddle.Classes
@@ -14,34 +16,32 @@
1416
1517module Language.Javascript.JSaddle.Classes (
1618 -- * Type classes to convert Haskell data to JavaScript
17− MakeVal(..)
18− , MakeString(..)
19− , MakeArgs(..)
19+ ToJSVal(..)
20+ , ToJSString(..)
2021 , MakeObject(..)
2122) where
2223
23−import Control.Monad.IO.Class (MonadIO)
2424import Language.Javascript.JSaddle.Types
25− (Object(..), JSString, JSVal(..))
26−import Language.Javascript.JSaddle.Monad (JSM)
25+ (JSM, Object(..), JSString, JSVal)
2726
2827-- | Anything that can be used to make a JavaScript value reference
29−class MakeVal a where
30− makeVal :: a -> JSM JSVal
28+class ToJSVal a where
29+ toJSVal :: a -> JSM JSVal
3130
3231-- | Anything that can be used to make a JavaScript string reference
33−class MakeString a where
34− makeString :: a -> JSString
35−
36−-- | Anything that can be used to make a list of JavaScript value
37−-- references for use as function arguments
38−class MakeArgs this where
39− makeArgs :: this -> JSM [JSVal]
32+class ToJSVal a => ToJSString a where
33+ toJSString :: a -> JSString
4034
4135-- | Anything that can be used to make a JavaScript object reference
4236class MakeObject this where
4337 makeObject :: this -> JSM Object
4438
45−instance MakeVal Object where
46− makeVal (Object r) = return r
47− {-# INLINE makeVal #-}
39+instance ToJSVal Object where
40+ toJSVal (Object r) = return r
41+ {-# INLINE toJSVal #-}
42+
43+-- | If we already have a Object we are fine
44+instance MakeObject Object where
45+ makeObject = return
46+ {-# INLINE makeObject #-}
47+
src/Language/Javascript/JSaddle/Evaluate.hs+18−18
@@ -23,22 +23,22 @@ module Language.Javascript.JSaddle.Evaluate (
2323
2424import Control.Monad.Trans.Reader (ask)
2525import Control.Monad.IO.Class (MonadIO(..))
26−import Language.Javascript.JSaddle.Types
27− (JSString, JSVal, Object(..),
28− MutableJSArray)
26+import Language.Javascript.JSaddle.Types (JSVal)
2927#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
3028import GHCJS.Types (nullRef)
3129import GHCJS.Marshal.Pure (pFromJSVal)
30+import Language.Javascript.JSaddle.Types (JSString)
3231#else
3332import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
3433 (jsevaluatescript)
35−import Foreign (nullPtr)
34+import Language.Javascript.JSaddle.Native
35+ (makeNewJSVal, withObject, withJSString)
3636#endif
3737import Language.Javascript.JSaddle.Exception (rethrow)
38−import Language.Javascript.JSaddle.Value ()
39−import Language.Javascript.JSaddle.Object ()
38+import Language.Javascript.JSaddle.String (nullJSString)
39+import Language.Javascript.JSaddle.Object (nullObject)
4040import Language.Javascript.JSaddle.Classes
41− (MakeObject(..), MakeString(..))
41+ (MakeObject(..), ToJSString(..))
4242import Language.Javascript.JSaddle.Monad (JSM)
4343
4444-- | Evaluates a script (like eval in java script). Unlike 'eval' this function lets you
@@ -46,22 +46,27 @@ import Language.Javascript.JSaddle.Monad (JSM)
4646--
4747-- >>> testJSaddle $ (evaluateScript "\n\n{" global "FileName" 53 >>= valToText) `catch` \(JSException e) -> array (e,e!"sourceURL", e!"line") >>= valToText
4848-- SyntaxError: Expected token '}',FileName,55
49−evaluateScript :: (MakeString script, MakeObject this, MakeString url)
49+evaluateScript :: (ToJSString script, MakeObject this, ToJSString url)
5050 => script -- ^ JavaScript to evaluate
5151 -> this
5252 -> url
5353 -> Int -- ^ The Line number of the first line of the script
5454 -> JSM JSVal
5555#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
56−evaluateScript script this url line = liftIO $ js_eval (makeString script)
56+evaluateScript script this url line = liftIO $ js_eval (toJSString script)
5757{-# INLINE evaluateScript #-}
5858foreign import javascript unsafe "$r = eval($1);"
5959 js_eval :: JSString -> IO JSVal
6060#elif defined(USE_WEBKIT)
6161evaluateScript script this url line = do
6262 gctxt <- ask
63− (Object thisr) <- makeObject this
64− rethrow $ liftIO . jsevaluatescript gctxt (makeString script) thisr (makeString url) line
63+ rthis <- makeObject this
64+ result <-
65+ withJSString (toJSString script) $ \script' ->
66+ withObject rthis $ \this' ->
67+ withJSString (toJSString url) $ \url' ->
68+ rethrow $ liftIO . jsevaluatescript gctxt script' this' url' line
69+ makeNewJSVal result
6570{-# INLINE evaluateScript #-}
6671#else
6772evaluateScript = undefined
@@ -71,13 +76,8 @@ evaluateScript = undefined
7176--
7277-- >>> testJSaddle $ eval "1+1"
7378-- 2
74−eval :: MakeString script
79+eval :: ToJSString script
7580 => script -- ^ JavaScript to evaluate
7681 -> JSM JSVal
77−#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
78−eval script = evaluateScript script (Object nullRef) (pFromJSVal nullRef::JSString) 1
79−{-# INLINE eval #-}
80−#else
81−eval script = evaluateScript script (Object nullPtr) (nullPtr::JSString) 1
82+eval script = evaluateScript script nullObject nullJSString 1
8283{-# INLINE eval #-}
83−#endif
src/Language/Javascript/JSaddle/Exception.hs−1
@@ -22,7 +22,6 @@ import Language.Javascript.JSaddle.Types
2222import Data.Typeable (Typeable)
2323import Language.Javascript.JSaddle.Monad (catchval, JSM)
2424import Control.Monad.IO.Class (MonadIO(..))
25−import Data.Text (Text)
2625
2726newtype JSException = JSException JSVal deriving (Typeable)
2827
src/Language/Javascript/JSaddle/Monad.hs+42−18
@@ -13,27 +13,32 @@
1313
1414module Language.Javascript.JSaddle.Monad (
1515 -- * Types
16− JSM(..)
16+ JSM
1717 , JSContextRef
1818
1919 -- * Running JSaddle given a DOM Window
2020 , runJSaddle
21− , runJSaddle_
2221
2322 -- * Exception Handling
2423 , catchval
2524 , catch
25+ , bracket
26+
27+ -- * GUI thread support
28+ , postGUIAsyncJS
29+ , postGUISyncJS
2630) where
2731
2832import Prelude hiding (catch, read)
2933import Control.Monad.Trans.Reader (runReaderT, ask, ReaderT(..))
3034import Language.Javascript.JSaddle.Types
31− (JSVal(..), MutableJSArray(..), JSContextRef)
35+ (JSM, JSVal, MutableJSArray, JSContextRef)
3236import Control.Monad.IO.Class (MonadIO(..))
3337#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
3438import GHCJS.Types (isUndefined, isNull)
3539import qualified JavaScript.Array as Array (create, read)
3640#else
41+import Language.Javascript.JSaddle.Native (makeNewJSVal)
3742import Foreign (nullPtr, alloca)
3843import Foreign.Storable (Storable(..))
3944import Graphics.UI.Gtk.WebKit.Types
@@ -42,19 +47,9 @@ import Graphics.UI.Gtk.WebKit.WebView
4247 (webViewGetMainFrame)
4348import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
4449 (webFrameGetGlobalContext)
50+import Graphics.UI.Gtk.General.General (postGUIAsync, postGUISync)
4551#endif
46−import qualified Control.Exception as E (Exception, catch)
47−import Control.Monad (void)
48−
49−-- | The @JSM@ monad keeps track of the JavaScript context.
50−--
51−-- Given a @JSM@ function and a 'JSContextRef' you can run the
52−-- function like this...
53−--
54−-- > runReaderT jsmFunction javaScriptContext
55−--
56−-- For an example of how to set up WebKitGTK+ see tests/TestJSaddle.hs
57−type JSM = ReaderT JSContextRef IO
52+import qualified Control.Exception as E (Exception, catch, bracket)
5853
5954-- | Wrapped version of 'E.catch' that runs in a MonadIO that works
6055-- a bit better with 'JSM'
@@ -67,6 +62,17 @@ t `catch` c = do
6762 liftIO (runReaderT t r `E.catch` \e -> runReaderT (c e) r)
6863{-# INLINE catch #-}
6964
65+-- | Wrapped version of 'E.bracket' that runs in a MonadIO that works
66+-- a bit better with 'JSM'
67+bracket :: MonadIO m => ReaderT r IO a -> (a -> ReaderT r IO b) -> (a -> ReaderT r IO c) -> ReaderT r m c
68+bracket aquire release f = do
69+ r <- ask
70+ liftIO $ E.bracket
71+ (runReaderT aquire r)
72+ (\x -> runReaderT (release x) r)
73+ (\x -> runReaderT (f x) r)
74+{-# INLINE bracket #-}
75+
7076-- | Handle JavaScriptCore functions that take a MutableJSArray in order
7177-- to throw exceptions.
7278catchval :: (MutableJSArray -> JSM a) -> (JSVal -> JSM a) -> JSM a
@@ -86,7 +92,7 @@ catchval f catcher = do
8692 exc <- liftIO $ peek pexc
8793 if exc == nullPtr
8894 then return result
89− else catcher exc
95+ else makeNewJSVal exc >>= catcher
9096#endif
9197
9298#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
@@ -100,5 +106,23 @@ runJSaddle webView f = do
100106#endif
101107{-# INLINE runJSaddle #-}
102108
103−runJSaddle_ w f = void $ runJSaddle w f
104−{-# INLINE runJSaddle_ #-}
109+postGUIAsyncJS :: JSM () -> JSM ()
110+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
111+postGUIAsyncJS = id
112+#else
113+postGUIAsyncJS f = do
114+ r <- ask
115+ liftIO . postGUIAsync $ runReaderT f r
116+#endif
117+{-# INLINE postGUIAsyncJS #-}
118+
119+postGUISyncJS :: JSM a -> JSM a
120+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
121+postGUISyncJS = id
122+#else
123+postGUISyncJS f = do
124+ r <- ask
125+ liftIO . postGUISync $ runReaderT f r
126+#endif
127+{-# INLINE postGUISyncJS #-}
128+
src/Language/Javascript/JSaddle/Native.hsnew file+97
@@ -0,0 +1,97 @@
1+{-# LANGUAGE CPP #-}
2+-----------------------------------------------------------------------------
3+--
4+-- Module : Language.Javascript.JSaddle.Native
5+-- Copyright : (c) Hamish Mackenzie
6+-- License : MIT
7+--
8+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
9+--
10+-- |
11+--
12+-----------------------------------------------------------------------------
13+
14+module Language.Javascript.JSaddle.Native (
15+#if !defined(ghcjs_HOST_OS) || !defined(USE_JAVASCRIPTFFI)
16+ makeNewJSVal
17+ , makeNewJSString
18+ , wrapJSString
19+ , withJSVal
20+ , withJSVals
21+ , withObject
22+ , withJSString
23+ , withToJSVal
24+#endif
25+) where
26+
27+#if !defined(ghcjs_HOST_OS) || !defined(USE_JAVASCRIPTFFI)
28+import Control.Monad.Trans.Reader (ask)
29+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
30+ (JSObjectRef, OpaqueJSString, JSStringRef, OpaqueJSContext,
31+ OpaqueJSValue, JSValueRef)
32+import Foreign.ForeignPtr
33+ (touchForeignPtr, FinalizerPtr, newForeignPtr, FinalizerEnvPtr,
34+ newForeignPtrEnv, ForeignPtr)
35+import Control.Monad.IO.Class (MonadIO, MonadIO(..))
36+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
37+ (jsvalueprotect)
38+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
39+ (jsstringretain)
40+import Language.Javascript.JSaddle.Types
41+ (JSM, JSString, Object(..), JSVal)
42+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
43+import Language.Javascript.JSaddle.Classes (ToJSVal(..), ToJSVal)
44+
45+makeNewJSVal :: JSValueRef -> JSM (ForeignPtr OpaqueJSValue)
46+makeNewJSVal val = do
47+ ctx <- ask
48+ liftIO $ do
49+ jsvalueprotect ctx val
50+ newForeignPtrEnv jsValueUnprotect ctx val
51+
52+foreign import ccall unsafe "&JSValueUnprotect"
53+ jsValueUnprotect :: FinalizerEnvPtr OpaqueJSContext OpaqueJSValue
54+
55+makeNewJSString :: MonadIO m => JSStringRef -> m (ForeignPtr OpaqueJSString)
56+makeNewJSString s =
57+ liftIO $ do
58+ s' <- jsstringretain s
59+ newForeignPtr jsStringRelease s'
60+
61+wrapJSString :: MonadIO m => JSStringRef -> m (ForeignPtr OpaqueJSString)
62+wrapJSString s = liftIO $ newForeignPtr jsStringRelease s
63+
64+foreign import ccall unsafe "&JSStringRelease"
65+ jsStringRelease :: FinalizerPtr OpaqueJSString
66+
67+withJSVal :: MonadIO m => JSVal -> (JSValueRef -> m a) -> m a
68+withJSVal v f =
69+ do result <- f (unsafeForeignPtrToPtr v)
70+ liftIO $ touchForeignPtr v
71+ return result
72+
73+withJSVals :: MonadIO m => [JSVal] -> ([JSValueRef] -> m a) -> m a
74+withJSVals v f =
75+ do result <- f (map unsafeForeignPtrToPtr v)
76+ liftIO $ mapM_ touchForeignPtr v
77+ return result
78+
79+withObject :: MonadIO m => Object -> (JSObjectRef -> m a) -> m a
80+withObject (Object o) f = do
81+ result <- f (unsafeForeignPtrToPtr o)
82+ liftIO $ touchForeignPtr o
83+ return result
84+
85+withJSString :: MonadIO m => JSString -> (JSStringRef -> m a) -> m a
86+withJSString v f =
87+ do result <- f (unsafeForeignPtrToPtr v)
88+ liftIO $ touchForeignPtr v
89+ return result
90+
91+withToJSVal :: ToJSVal val => val -> (JSValueRef -> JSM a) -> JSM a
92+withToJSVal val f = do
93+ v <- toJSVal val
94+ withJSVal v f
95+{-# INLINE withToJSVal #-}
96+#endif
97+
src/Language/Javascript/JSaddle/Object.hs+173−90
@@ -3,10 +3,11 @@
33{-# LANGUAGE TypeSynonymInstances #-}
44{-# LANGUAGE ScopedTypeVariables #-}
55{-# LANGUAGE Rank2Types #-}
6−{-# LANGUAGE ForeignFunctionInterface #-}
76#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
7+{-# LANGUAGE ForeignFunctionInterface #-}
88{-# LANGUAGE JavaScriptFFI #-}
99#endif
10+{-# OPTIONS_GHC -fno-warn-orphans #-}
1011-----------------------------------------------------------------------------
1112--
1213-- Module : Language.Javascript.JSaddle.Object
@@ -28,7 +29,7 @@ module Language.Javascript.JSaddle.Object (
2829 , (!!)
2930 , js
3031 , jss
31− , JSF(..)
32+ , JSF
3233 , jsf
3334 , js0
3435 , js1
@@ -57,9 +58,11 @@ module Language.Javascript.JSaddle.Object (
5758 , obj
5859
5960 -- * Calling Haskell From JavaScript
61+ , Function(..)
6062 , function
63+ , freeFunction
6164 , fun
62− , JSCallAsFunction(..)
65+ , JSCallAsFunction
6366 -- ** Object Constructors
6467
6568 -- | There is no good way to support calling haskell code as a JavaScript
@@ -78,20 +81,23 @@ module Language.Javascript.JSaddle.Object (
7881
7982 -- * Enumerating Properties
8083 , propertyNames
84+ , properties
8185
8286 -- * Low level
8387 , objCallAsFunction
8488 , objCallAsConstructor
89+ , nullObject
8590) where
8691
8792import Prelude hiding ((!!))
8893import Language.Javascript.JSaddle.Types
8994 (JSPropertyNameArray, JSString, Object(..), MutableJSArray,
9095 JSVal, Index)
91−import Foreign.C.Types (CSize(..), CULong(..), CUInt(..), CULLong(..))
96+import Foreign.C.Types (CSize(..), CULong(..))
9297#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
9398import GHCJS.Types (nullRef, jsval)
94−import GHCJS.Foreign.Callback (syncCallback2, OnBlocked(..), Callback)
99+import GHCJS.Foreign.Callback
100+ (releaseCallback, syncCallback2, OnBlocked(..), Callback)
95101import GHCJS.Marshal.Pure (pFromJSVal)
96102import JavaScript.Array (JSArray)
97103import qualified JavaScript.Array as Array (toListIO, fromListIO)
@@ -104,39 +110,39 @@ import Data.Coerce (coerce)
104110import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
105111 (jsobjectmake, jspropertynamearraygetnameatindex,
106112 jspropertynamearraygetcount, jsobjectcopypropertynames,
107− jsobjectsetpropertyatindex, jsobjectgetpropertyatindex,
108113 jsobjectcallasconstructor, jsobjectmakearray,
109− jsobjectcallasfunction, jsobjectgetproperty, jsobjectsetproperty,
110− JSPropertyAttributes, JSObjectCallAsFunctionCallback,
114+ jsobjectcallasfunction,
115+ JSObjectCallAsFunctionCallback,
111116 jsobjectmakefunctionwithcallback, JSObjectCallAsFunctionCallback')
112117import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
113118 (jsvaluemakeundefined)
114119import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSContextRef
115120 (jscontextgetglobalobject)
116121import Foreign (peekArray, nullPtr, withArrayLen)
122+import Foreign.Ptr (freeHaskellFunPtr)
123+import Language.Javascript.JSaddle.Native
124+ (makeNewJSVal, wrapJSString, withJSVals, withObject, withJSString,
125+ withToJSVal)
126+import System.IO.Unsafe (unsafePerformIO)
127+import Foreign.ForeignPtr (newForeignPtr_)
117128#endif
118129import Language.Javascript.JSaddle.Exception (rethrow)
119130import Language.Javascript.JSaddle.Value
120− (JSUndefined, valMakeUndefined, valToObject)
131+ (JSUndefined, valToObject)
121132import Language.Javascript.JSaddle.Classes
122− (MakeVal(..), MakeString(..), MakeArgs(..), MakeObject(..))
133+ (ToJSVal(..), ToJSString(..), MakeObject(..))
134+import Language.Javascript.JSaddle.Arguments (MakeArgs(..))
123135import Language.Javascript.JSaddle.Monad
124136 (JSM)
125137import Control.Monad.Trans.Reader (runReaderT, ask)
126138import Control.Monad.IO.Class (MonadIO(..))
127139import qualified Control.Exception as E (catch)
128140import Control.Exception (SomeException)
129−import qualified Data.Text as T (pack)
130141import Foreign.Storable (Storable(..))
131142import Language.Javascript.JSaddle.Properties
132−import Control.Lens
133− (IndexPreservingGetter, to, (^.))
134−import Language.Javascript.JSaddle.String (textToStr)
135−
136−-- | If we already have a Object we are fine
137−instance MakeObject Object where
138− makeObject = return
139− {-# INLINE makeObject #-}
143+import Control.Lens (IndexPreservingGetter, to)
144+import Language.Javascript.JSaddle.String (nullJSString)
145+import Data.Text (Text)
140146
141147-- | Object can be made by evaluating a function in 'JSM' as long
142148-- as it returns something we can make into a Object.
@@ -149,7 +155,7 @@ instance MakeObject v => MakeObject (JSM v) where
149155-- >>> testJSaddle $ eval "'Hello World'.length"
150156-- >>> testJSaddle $ val "Hello World" ! "length"
151157-- 11
152−(!) :: (MakeObject this, MakeString name)
158+(!) :: (MakeObject this, ToJSString name)
153159 => this -- ^ Object to look on
154160 -> name -- ^ Name of the property to find
155161 -> JSM JSVal -- ^ Property reference
@@ -157,7 +163,7 @@ this ! name = do
157163 rthis <- makeObject this
158164 rethrow $ objGetPropertyByName rthis rname
159165 where
160− rname = makeString name
166+ rname = toJSString name
161167{-# INLINE (!) #-}
162168
163169-- | Lookup a property based on its index.
@@ -181,7 +187,7 @@ this !! index = do
181187-- >>> testJSaddle $ eval "'Hello World'.length"
182188-- >>> testJSaddle $ val "Hello World" ^. js "length"
183189-- 11
184−js :: (MakeObject s, MakeString name)
190+js :: (MakeObject s, ToJSString name)
185191 => name -- ^ Name of the property to find
186192 -> IndexPreservingGetter s (JSM JSVal)
187193js name = to (!name)
@@ -194,7 +200,7 @@ js name = to (!name)
194200-- >>> testJSaddle $ eval "'Hello World'.length"
195201-- >>> testJSaddle $ val "Hello World" ^. js "length"
196202-- 11
197−jss :: (MakeString name, MakeVal val)
203+jss :: (ToJSString name, ToJSVal val)
198204 => name -- ^ Name of the property to find
199205 -> val
200206 -> forall o . MakeObject o => IndexPreservingGetter o (JSM ())
@@ -207,7 +213,7 @@ jss name val = to (\o -> o <# name $ val)
207213--
208214-- >>> testJSaddle $ val "Hello World" ^. jsf "indexOf" ["World"]
209215-- 6
210−jsf :: (MakeString name, MakeArgs args) => name -> args -> JSF
216+jsf :: (ToJSString name, MakeArgs args) => name -> args -> JSF
211217jsf name args = to (\o -> o # name $ args)
212218{-# INLINE jsf #-}
213219
@@ -220,7 +226,7 @@ type JSF = forall o . MakeObject o => IndexPreservingGetter o (JSM JSVal)
220226--
221227-- >>> testJSaddle $ val "Hello World" ^. js0 "toLowerCase"
222228-- hello world
223−js0 :: (MakeString name) => name -> JSF
229+js0 :: (ToJSString name) => name -> JSF
224230js0 name = jsf name ()
225231{-# INLINE js0 #-}
226232
@@ -230,31 +236,31 @@ js0 name = jsf name ()
230236--
231237-- >>> testJSaddle $ val "Hello World" ^. js1 "indexOf" "World"
232238-- 6
233−js1 :: (MakeString name, MakeVal a0) => name -> a0 -> JSF
239+js1 :: (ToJSString name, ToJSVal a0) => name -> a0 -> JSF
234240js1 name a0 = jsf name [a0]
235241{-# INLINE js1 #-}
236242
237243-- | Handy way to call a function that expects two arguments
238−js2 :: (MakeString name, MakeVal a0, MakeVal a1) => name -> a0 -> a1 -> JSF
244+js2 :: (ToJSString name, ToJSVal a0, ToJSVal a1) => name -> a0 -> a1 -> JSF
239245js2 name a0 a1 = jsf name (a0, a1)
240246{-# INLINE js2 #-}
241247
242248-- | Handy way to call a function that expects three arguments
243−js3 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2)
249+js3 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2)
244250 => name -> a0 -> a1 -> a2 -> JSF
245251js3 name a0 a1 a2 = jsf name (a0, a1, a2)
246252{-# INLINE js3 #-}
247253
248254-- | Handy way to call a function that expects four arguments
249−js4 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2,
250− MakeVal a3)
255+js4 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2,
256+ ToJSVal a3)
251257 => name -> a0 -> a1 -> a2 -> a3 -> JSF
252258js4 name a0 a1 a2 a3 = jsf name (a0, a1, a2, a3)
253259{-# INLINE js4 #-}
254260
255261-- | Handy way to call a function that expects five arguments
256−js5 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2,
257− MakeVal a3, MakeVal a4)
262+js5 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2,
263+ ToJSVal a3, ToJSVal a4)
258264 => name -> a0 -> a1 -> a2 -> a3 -> a4 -> JSF
259265js5 name a0 a1 a2 a3 a4 = jsf name (a0, a1, a2, a3, a4)
260266{-# INLINE js5 #-}
@@ -265,7 +271,7 @@ js5 name a0 a1 a2 a3 a4 = jsf name (a0, a1, a2, a3, a4)
265271-- >>> testJSaddle $ eval "w = console; w.log('Hello World')"
266272-- >>> testJSaddle $ do w <- jsg "console"; w ^. js "log" # ["Hello World"]
267273-- 11
268−jsg :: MakeString a => a -> JSM JSVal
274+jsg :: ToJSString a => a -> JSM JSVal
269275jsg name = global ! name
270276{-# INLINE jsg #-}
271277
@@ -275,7 +281,7 @@ jsg name = global ! name
275281--
276282-- >>> testJSaddle $ jsf "globalFunc" ["World"]
277283-- 6
278−jsgf :: (MakeString name, MakeArgs args) => name -> args -> JSM JSVal
284+jsgf :: (ToJSString name, MakeArgs args) => name -> args -> JSM JSVal
279285jsgf name = global # name
280286{-# INLINE jsgf #-}
281287
@@ -285,7 +291,7 @@ jsgf name = global # name
285291--
286292-- >>> testJSaddle $ js0 "globalFunc"
287293-- hello world
288−jsg0 :: (MakeString name) => name -> JSM JSVal
294+jsg0 :: (ToJSString name) => name -> JSM JSVal
289295jsg0 name = jsgf name ()
290296{-# INLINE jsg0 #-}
291297
@@ -295,31 +301,31 @@ jsg0 name = jsgf name ()
295301--
296302-- >>> testJSaddle $ jsg1 "globalFunc" "World"
297303-- 6
298−jsg1 :: (MakeString name, MakeVal a0) => name -> a0 -> JSM JSVal
304+jsg1 :: (ToJSString name, ToJSVal a0) => name -> a0 -> JSM JSVal
299305jsg1 name a0 = jsgf name [a0]
300306{-# INLINE jsg1 #-}
301307
302308-- | Handy way to call a function that expects two arguments
303−jsg2 :: (MakeString name, MakeVal a0, MakeVal a1) => name -> a0 -> a1 -> JSM JSVal
309+jsg2 :: (ToJSString name, ToJSVal a0, ToJSVal a1) => name -> a0 -> a1 -> JSM JSVal
304310jsg2 name a0 a1 = jsgf name (a0, a1)
305311{-# INLINE jsg2 #-}
306312
307313-- | Handy way to call a function that expects three arguments
308−jsg3 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2)
314+jsg3 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2)
309315 => name -> a0 -> a1 -> a2 -> JSM JSVal
310316jsg3 name a0 a1 a2 = jsgf name (a0, a1, a2)
311317{-# INLINE jsg3 #-}
312318
313319-- | Handy way to call a function that expects four arguments
314−jsg4 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2,
315− MakeVal a3)
320+jsg4 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2,
321+ ToJSVal a3)
316322 => name -> a0 -> a1 -> a2 -> a3 -> JSM JSVal
317323jsg4 name a0 a1 a2 a3 = jsgf name (a0, a1, a2, a3)
318324{-# INLINE jsg4 #-}
319325
320326-- | Handy way to call a function that expects five arguments
321−jsg5 :: (MakeString name, MakeVal a0, MakeVal a1, MakeVal a2,
322− MakeVal a3, MakeVal a4)
327+jsg5 :: (ToJSString name, ToJSVal a0, ToJSVal a1, ToJSVal a2,
328+ ToJSVal a3, ToJSVal a4)
323329 => name -> a0 -> a1 -> a2 -> a3 -> a4 -> JSM JSVal
324330jsg5 name a0 a1 a2 a3 a4 = jsgf name (a0, a1, a2, a3, a4)
325331{-# INLINE jsg5 #-}
@@ -330,7 +336,7 @@ jsg5 name a0 a1 a2 a3 a4 = jsgf name (a0, a1, a2, a3, a4)
330336-- >>> testJSaddle $ val "Hello World" # "indexOf" $ ["World"]
331337-- 6
332338infixr 2 #
333−(#) :: (MakeObject this, MakeString name, MakeArgs args)
339+(#) :: (MakeObject this, ToJSString name, MakeArgs args)
334340 => this -> name -> args -> JSM JSVal
335341(#) this name args = do
336342 rthis <- makeObject this
@@ -359,7 +365,7 @@ infixr 2 ##
359365-- >>> testJSaddle $ do {j <- eval "({})"; j <# "x" 1; j!"x"}
360366-- 1
361367infixr 1 <#
362−(<#) :: (MakeObject this, MakeString name, MakeVal val)
368+(<#) :: (MakeObject this, ToJSString name, ToJSVal val)
363369 => this -- ^ Object to set the property on
364370 -> name -- ^ Name of the property to set
365371 -> val -- ^ Value to set it to
@@ -375,7 +381,7 @@ infixr 1 <#
375381-- >>> testJSaddle $ do {j <- eval "({})"; j <## 6 1; j!!6}
376382-- 1
377383infixr 1 <##
378−(<##) :: (MakeObject this, MakeVal val)
384+(<##) :: (MakeObject this, ToJSVal val)
379385 => this -- ^ Object to set the property on
380386 -> Index -- ^ Index of the property to set
381387 -> val -- ^ Value to set it to
@@ -406,10 +412,10 @@ new constructor args = do
406412-- >>> testJSaddle $ eval "(function(){return this;}).apply('Hello', [])"
407413-- >>> testJSaddle $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }
408414-- Hello
409−call :: (MakeObject function, MakeObject this, MakeArgs args)
410− => function -> this -> args -> JSM JSVal
411−call function this args = do
412− rfunction <- makeObject function
415+call :: (MakeObject f, MakeObject this, MakeArgs args)
416+ => f -> this -> args -> JSM JSVal
417+call f this args = do
418+ rfunction <- makeObject f
413419 rthis <- makeObject this
414420 rethrow $ objCallAsFunction rfunction rthis args
415421{-# INLINE call #-}
@@ -425,7 +431,7 @@ obj = liftIO Object.create
425431#else
426432obj = do
427433 gctxt <- ask
428− liftIO $ Object <$> jsobjectmake gctxt nullPtr nullPtr
434+ Object <$> ((liftIO $ jsobjectmake gctxt nullPtr nullPtr) >>= makeNewJSVal)
429435#endif
430436{-# INLINE obj #-}
431437
@@ -455,56 +461,74 @@ foreign import ccall "wrapper"
455461 mkJSObjectCallAsFunctionCallback :: JSObjectCallAsFunctionCallback' -> IO JSObjectCallAsFunctionCallback
456462#endif
457463
464+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI) || !defined(USE_WEBKIT)
465+type HaskellCallback = Callback (JSVal -> JSVal -> IO ())
466+#else
467+type HaskellCallback = JSObjectCallAsFunctionCallback
468+#endif
469+
470+data Function = Function {functionCallback :: HaskellCallback, functionObject :: Object}
471+
458472-- ^ Make a JavaScript function object that wraps a Haskell function.
459−function :: MakeString name
473+function :: ToJSString name
460474 => name -- ^ Name of the function
461475 -> JSCallAsFunction -- ^ Haskell function to call
462− -> JSM Object -- ^ Returns a JavaScript function object that will
476+ -> JSM Function -- ^ Returns a JavaScript function object that will
463477 -- call the Haskell one when it is called
464478#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
465479function name f = liftIO $ do
466480 callback <- syncCallback2 ContinueAsync $ \this args -> do
467481 rargs <- Array.toListIO (coerce args)
468482 runReaderT (f this this rargs) () -- TODO pass function object through
469− makeFunctionWithCallback (makeString name) callback
483+ Function callback <$> makeFunctionWithCallback (toJSString name) callback
470484foreign import javascript unsafe "$r = function () { $2(this, arguments); }"
471485 makeFunctionWithCallback :: JSString -> Callback (JSVal -> JSVal -> IO ()) -> IO Object
472486#elif defined(USE_WEBKIT)
473487function name f = do
474488 gctxt <- ask
475− callback <- liftIO $ mkJSObjectCallAsFunctionCallback wrap
476− liftIO $ Object <$> jsobjectmakefunctionwithcallback gctxt (makeString name) callback
489+ callback <- liftIO $ mkJSObjectCallAsFunctionCallback (wrap gctxt)
490+ withJSString (toJSString name) $ \name' ->
491+ Function callback . Object <$>
492+ ((liftIO $ jsobjectmakefunctionwithcallback gctxt name' callback) >>= makeNewJSVal)
477493 where
478− wrap ctx fobj this argc argv exception = do
479− args <- peekArray (fromIntegral argc) argv
480− (`runReaderT` ctx) $
481− f fobj this args >>= makeVal
494+ wrap gctxt _ctx fobj' this' argc argv exception = do
495+ args' <- peekArray (fromIntegral argc) argv
496+ (`runReaderT` gctxt) $ do
497+ fobj <- makeNewJSVal fobj'
498+ this <- makeNewJSVal this'
499+ args <- mapM makeNewJSVal args'
500+ f fobj this args
501+ liftIO $ jsvaluemakeundefined gctxt
482502 `E.catch` \(e :: SomeException) -> do
483− str <- runReaderT (makeVal . T.pack $ show e) ctx
484− poke exception str
485− jsvaluemakeundefined ctx
503+ (`runReaderT` gctxt) $ do
504+ withToJSVal (show e) $ liftIO . poke exception
505+ liftIO $ jsvaluemakeundefined gctxt
486506#else
487507function = undefined
488508#endif
489509
510+freeFunction :: MonadIO m => Function -> m ()
511+freeFunction (Function callback _) = liftIO $
512+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI) || !defined(USE_WEBKIT)
513+ releaseCallback callback
514+#else
515+ freeHaskellFunPtr callback
516+#endif
517+
518+instance ToJSVal Function where
519+ toJSVal (Function _ f) = toJSVal f
520+ {-# INLINE toJSVal #-}
521+
490522-- | A callback to Haskell can be used as a JavaScript value. This will create
491523-- an anonymous JavaScript function object. Use 'function' to create one with
492524-- a name.
493−instance MakeVal JSCallAsFunction where
494−#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
495− makeVal f = jsval <$> function (pFromJSVal nullRef :: JSString) f
496−#else
497− makeVal f = function (nullPtr :: JSString) f >>= makeVal
498−#endif
499− {-# INLINE makeVal #-}
525+instance ToJSVal JSCallAsFunction where
526+ toJSVal f = functionObject <$> function nullJSString f >>= toJSVal
527+ {-# INLINE toJSVal #-}
500528
501529instance MakeArgs JSCallAsFunction where
502530 makeArgs f = do
503−#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
504− rarg <- jsval <$> function (pFromJSVal nullRef :: JSString) f
505−#else
506− rarg <- function (nullPtr:: JSString) f >>= makeVal
507−#endif
531+ rarg <- functionObject <$> function nullJSString f >>= toJSVal
508532 return [rarg]
509533 {-# INLINE makeArgs #-}
510534
@@ -517,8 +541,11 @@ makeArray args exceptions = do
517541makeArray args exceptions = do
518542 gctxt <- ask
519543 rargs <- makeArgs args
520− liftIO $ withArrayLen rargs $ \ len ptr ->
521− Object <$> jsobjectmakearray gctxt (fromIntegral len) ptr exceptions
544+ result <-
545+ withJSVals rargs $ \rargs' ->
546+ liftIO $ withArrayLen rargs' $ \ len ptr ->
547+ jsobjectmakearray gctxt (fromIntegral len) ptr exceptions
548+ Object <$> makeNewJSVal result
522549#endif
523550{-# INLINE makeArray #-}
524551
@@ -533,6 +560,43 @@ makeArray args exceptions = do
533560array :: MakeArgs args => args -> JSM Object
534561array = rethrow . makeArray
535562
563+-- Make an array out of various lists
564+instance ToJSVal [JSVal] where
565+ toJSVal = toJSVal . array
566+ {-# INLINE toJSVal #-}
567+
568+instance ToJSVal [JSM JSVal] where
569+ toJSVal = toJSVal . array
570+ {-# INLINE toJSVal #-}
571+
572+instance ToJSVal [Double] where
573+ toJSVal = toJSVal . array
574+ {-# INLINE toJSVal #-}
575+
576+instance ToJSVal [Float] where
577+ toJSVal = toJSVal . array
578+ {-# INLINE toJSVal #-}
579+
580+instance ToJSVal [Int] where
581+ toJSVal = toJSVal . array
582+ {-# INLINE toJSVal #-}
583+
584+instance ToJSVal [JSString] where
585+ toJSVal = toJSVal . array
586+ {-# INLINE toJSVal #-}
587+
588+instance ToJSVal [String] where
589+ toJSVal = toJSVal . array
590+ {-# INLINE toJSVal #-}
591+
592+instance ToJSVal [Text] where
593+ toJSVal = toJSVal . array
594+ {-# INLINE toJSVal #-}
595+
596+instance ToJSVal [Bool] where
597+ toJSVal = toJSVal . array
598+ {-# INLINE toJSVal #-}
599+
536600-- | JavaScript's global object
537601global :: JSM Object
538602#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
@@ -541,7 +605,10 @@ global = liftIO js_window
541605foreign import javascript unsafe "$r = window"
542606 js_window :: IO Object
543607#elif defined(USE_WEBKIT)
544−global = ask >>= (liftIO . fmap Object . jscontextgetglobalobject)
608+global = do
609+ gctxt <- ask
610+ result <- liftIO $ jscontextgetglobalobject gctxt
611+ Object <$> makeNewJSVal result
545612{-# INLINE global #-}
546613#else
547614global = undefined
@@ -552,8 +619,9 @@ global = undefined
552619copyPropertyNames :: MakeObject this => this -> JSM JSPropertyNameArray
553620copyPropertyNames this = do
554621 gctxt <- ask
555− Object rthis <- makeObject this
556− liftIO $ jsobjectcopypropertynames gctxt rthis
622+ this' <- makeObject this
623+ withObject this' $ \rthis ->
624+ liftIO $ jsobjectcopypropertynames gctxt rthis
557625{-# INLINE copyPropertyNames #-}
558626
559627-- | Get the number of names in a property name array
@@ -563,7 +631,7 @@ propertyNamesCount names = liftIO $ jspropertynamearraygetcount names
563631
564632-- | Get a name out of a property name array
565633propertyNamesAt :: MonadIO m => JSPropertyNameArray -> CSize -> m JSString
566−propertyNamesAt names index = liftIO $ jspropertynamearraygetnameatindex names index
634+propertyNamesAt names index = liftIO $ jspropertynamearraygetnameatindex names index >>= wrapJSString
567635{-# INLINE propertyNamesAt #-}
568636
569637-- | Convert property array to a list
@@ -600,18 +668,23 @@ objCallAsFunction :: MakeArgs args
600668 -> MutableJSArray
601669 -> JSM JSVal
602670#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
603−objCallAsFunction function this args exceptions = do
671+objCallAsFunction f this args exceptions = do
604672 rargs <- makeArgs args >>= liftIO . Array.fromListIO
605− liftIO $ js_apply function this rargs exceptions
673+ liftIO $ js_apply f this rargs exceptions
606674{-# INLINE objCallAsFunction #-}
607675foreign import javascript unsafe "try { $r = $1.apply($2, $3) } catch(e) { $4[0] = e }"
608676 js_apply :: Object -> Object -> MutableJSArray -> MutableJSArray -> IO JSVal
609677#elif defined(USE_WEBKIT)
610−objCallAsFunction (Object function) (Object this) args exceptions = do
678+objCallAsFunction f this args exceptions = do
611679 gctxt <- ask
612680 rargs <- makeArgs args
613− liftIO $ withArrayLen rargs $ \ largs pargs ->
614− jsobjectcallasfunction gctxt function this (fromIntegral largs) pargs exceptions
681+ result <-
682+ withObject f $ \rfunction ->
683+ withObject this $ \rthis ->
684+ withJSVals rargs $ \rargs' ->
685+ liftIO $ withArrayLen rargs' $ \ largs pargs ->
686+ jsobjectcallasfunction gctxt rfunction rthis (fromIntegral largs) pargs exceptions
687+ makeNewJSVal result
615688{-# INLINE objCallAsFunction #-}
616689#else
617690objCallAsFunction = undefined
@@ -627,9 +700,9 @@ objCallAsConstructor :: MakeArgs args
627700 -> MutableJSArray
628701 -> JSM JSVal
629702#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
630−objCallAsConstructor function args exceptions = do
703+objCallAsConstructor f args exceptions = do
631704 rargs <- makeArgs args >>= liftIO . Array.fromListIO
632− liftIO $ js_new function rargs exceptions
705+ liftIO $ js_new f rargs exceptions
633706foreign import javascript unsafe "\
634707 try {\
635708 switch($2.length) {\
@@ -659,15 +732,25 @@ foreign import javascript unsafe "\
659732 }"
660733 js_new :: Object -> MutableJSArray -> MutableJSArray -> IO JSVal
661734#elif defined(USE_WEBKIT)
662−objCallAsConstructor (Object function) args exceptions = do
735+objCallAsConstructor f args exceptions = do
663736 gctxt <- ask
664737 rargs <- makeArgs args
665− liftIO $ withArrayLen rargs $ \ largs pargs ->
666− jsobjectcallasconstructor gctxt function (fromIntegral largs) pargs exceptions
738+ result <-
739+ withObject f $ \rfunction ->
740+ withJSVals rargs $ \rargs' ->
741+ liftIO $ withArrayLen rargs' $ \ largs pargs ->
742+ jsobjectcallasconstructor gctxt rfunction (fromIntegral largs) pargs exceptions
743+ makeNewJSVal result
667744#else
668745objCallAsConstructor = undefined
669746#endif
670747
748+nullObject :: Object
749+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
750+nullObject = Object nullRef
751+#else
752+nullObject = Object . unsafePerformIO $ newForeignPtr_ nullPtr
753+#endif
671754
672755
673756
src/Language/Javascript/JSaddle/Properties.hs+37−40
@@ -29,46 +29,45 @@ module Language.Javascript.JSaddle.Properties (
2929 , objSetPropertyAtIndex
3030) where
3131
32−import Control.Applicative ((<$>))
3332import Language.Javascript.JSaddle.Classes
34− (MakeObject(..), MakeVal(..),
35− MakeArgs(..), MakeString(..))
33+ (ToJSVal(..), ToJSString(..))
3634import Language.Javascript.JSaddle.Monad (JSM)
3735import Language.Javascript.JSaddle.Types
3836 (MutableJSArray, Object(..), JSPropertyAttributes,
39− Index(..), JSString)
37+ Index)
4038#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
39+import Language.Javascript.JSaddle.Types (JSString)
4140#else
4241import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
4342 (jsobjectgetpropertyatindex, jsobjectgetproperty,
44− jsobjectsetpropertyatindex, jsobjectsetproperty,
45− JSPropertyAttributes)
43+ jsobjectsetpropertyatindex, jsobjectsetproperty)
44+import Language.Javascript.JSaddle.Native
45+ (makeNewJSVal, withObject, withJSString, withToJSVal)
4646#endif
4747import Control.Monad.Trans.Reader (ask)
4848import Control.Monad.IO.Class (MonadIO(..))
49−import Language.Javascript.JSaddle.Exception (rethrow)
5049import Language.Javascript.JSaddle.Value (JSVal)
5150import Language.Javascript.JSaddle.Arguments ()
5251import Language.Javascript.JSaddle.String ()
5352
5453-- | Get a property value given the object and the name of the property.
55−objGetPropertyByName :: MakeString name
54+objGetPropertyByName :: ToJSString name
5655 => Object -- ^ object to find the property on.
5756 -> name -- ^ name of the property.
5857 -> MutableJSArray -- ^ exception if one is raised.
5958 -> JSM JSVal -- ^ returns the property value.
60−#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
61−objGetPropertyByName this name = liftIO . js_tryGetProp (makeString name) this
59+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
60+objGetPropertyByName this name = liftIO . js_tryGetProp (toJSString name) this
6261{-# INLINE objGetPropertyByName #-}
6362foreign import javascript unsafe "try { $r=$2[$1] } catch(e) { $3[0] = e }"
6463 js_tryGetProp :: JSString -> Object -> MutableJSArray -> IO JSVal
65−#elif defined(USE_WEBKIT)
66−objGetPropertyByName (Object this) name exceptions = do
64+#else
65+objGetPropertyByName this name exceptions = do
6766 gctxt <- ask
68− liftIO $ jsobjectgetproperty gctxt this (makeString name) exceptions
67+ withObject this $ \rthis ->
68+ withJSString (toJSString name) $ \name' ->
69+ (liftIO $ jsobjectgetproperty gctxt rthis name' exceptions) >>= makeNewJSVal
6970{-# INLINE objGetPropertyByName #-}
70−#else
71−objGetPropertyByName = undefined
7271#endif
7372
7473-- | Get a property value given the object and the index of the property.
@@ -76,66 +75,64 @@ objGetPropertyAtIndex :: Object -- ^ object to find the property on.
7675 -> Index -- ^ index of the property.
7776 -> MutableJSArray -- ^ exception if one is raised.
7877 -> JSM JSVal -- ^ returns the property value.
79−#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
78+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
8079objGetPropertyAtIndex this index = liftIO . js_tryIndex index this
8180{-# INLINE objGetPropertyAtIndex #-}
8281foreign import javascript unsafe "try { $r=$2[$1] } catch(e) { $3[0] = e }"
8382 js_tryIndex :: Index -> Object -> MutableJSArray -> IO JSVal
84−#elif defined(USE_WEBKIT)
85−objGetPropertyAtIndex (Object this) index exceptions = do
83+#else
84+objGetPropertyAtIndex this index exceptions = do
8685 gctxt <- ask
87− liftIO $ jsobjectgetpropertyatindex gctxt this index exceptions
86+ withObject this $ \rthis ->
87+ (liftIO $ jsobjectgetpropertyatindex gctxt rthis index exceptions) >>= makeNewJSVal
8888{-# INLINE objGetPropertyAtIndex #-}
89−#else
90−objGetPropertyAtIndex = undefined
9189#endif
9290
9391-- | Set a property value given the object and the name of the property.
94−objSetPropertyByName :: (MakeString name, MakeVal val)
92+objSetPropertyByName :: (ToJSString name, ToJSVal val)
9593 => Object -- ^ object to set the property on.
9694 -> name -- ^ name of the property.
9795 -> val -- ^ new value to set the property to.
9896 -> JSPropertyAttributes -- ^ property attributes to give the property.
9997 -> MutableJSArray -- ^ exception if one is raised.
10098 -> JSM ()
101−#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
99+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
102100objSetPropertyByName this name val attributes exceptions = do
103− vref <- makeVal val
104− liftIO $ js_trySetProp (makeString name) this vref exceptions
101+ vref <- toJSVal val
102+ liftIO $ js_trySetProp (toJSString name) this vref exceptions
105103{-# INLINE objSetPropertyByName #-}
106104foreign import javascript unsafe "try { $2[$1]=$3 } catch(e) { $4[0] = e }"
107105 js_trySetProp :: JSString -> Object -> JSVal -> MutableJSArray -> IO ()
108−#elif defined(USE_WEBKIT)
109−objSetPropertyByName (Object this) name val attributes exceptions = do
106+#else
107+objSetPropertyByName this name val attributes exceptions = do
110108 gctxt <- ask
111− vref <- makeVal val
112− liftIO $ jsobjectsetproperty gctxt this (makeString name) vref attributes exceptions
109+ withObject this $ \rthis ->
110+ withJSString (toJSString name) $ \name' ->
111+ withToJSVal val $ \rval ->
112+ liftIO $ jsobjectsetproperty gctxt rthis name' rval attributes exceptions
113113{-# INLINE objSetPropertyByName #-}
114−#else
115−objSetPropertyByName = undefined
116114#endif
117115
118116-- | Set a property value given the object and the index of the property.
119−objSetPropertyAtIndex :: (MakeVal val)
117+objSetPropertyAtIndex :: (ToJSVal val)
120118 => Object -- ^ object to find property on.
121119 -> Index -- ^ index of the property.
122120 -> val -- ^ new value to set the property to.
123121 -> MutableJSArray -- ^ exception if one is raised.
124122 -> JSM ()
125−#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
123+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
126124objSetPropertyAtIndex this index val exceptions = do
127− vref <- makeVal val
125+ vref <- toJSVal val
128126 liftIO $ js_trySetAtIndex index this vref exceptions
129127{-# INLINE objSetPropertyAtIndex #-}
130128foreign import javascript unsafe "try { $2[$1]=$3 } catch(e) { $4[0] = e }"
131129 js_trySetAtIndex :: Index -> Object -> JSVal -> MutableJSArray -> IO ()
132−#elif defined(USE_WEBKIT)
133−objSetPropertyAtIndex (Object this) index val exceptions = do
130+#else
131+objSetPropertyAtIndex this index val exceptions = do
134132 gctxt <- ask
135− vref <- makeVal val
136− liftIO $ jsobjectsetpropertyatindex gctxt this index vref exceptions
133+ withObject this $ \rthis ->
134+ withToJSVal val $ \rval ->
135+ liftIO $ jsobjectsetpropertyatindex gctxt rthis index rval exceptions
137136{-# INLINE objSetPropertyAtIndex #-}
138−#else
139−objSetPropertyAtIndex = undefined
140137#endif
141138
src/Language/Javascript/JSaddle/String.hs+19−22
@@ -15,49 +15,41 @@
1515
1616module Language.Javascript.JSaddle.String (
1717 JSString
18− , MakeString(..)
18+ , ToJSString(..)
1919
2020 -- * Data.Text helpers
2121 , strToText
2222 , textToStr
23+ , nullJSString
2324) where
2425
2526import Data.Text (Text)
26−import qualified Data.Text as T (unpack, pack)
2727import Control.Monad.IO.Class (MonadIO(..))
2828import Language.Javascript.JSaddle.Types (JSString)
2929#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
3030import Data.JSString.Text (textFromJSString, textToJSString)
31+import GHCJS.Marshal.Internal (PFromJSVal(..))
32+import GHCJS.Types (nullRef)
3133#else
3234import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
3335 (jsstringcreatewithcharacters, jsstringgetcharactersptr,
34− jsstringgetlength, jsstringcreatewithutf8cstring)
35−#endif
36+ jsstringgetlength)
37+import Language.Javascript.JSaddle.Native (makeNewJSString, withJSString)
38+import System.IO.Unsafe (unsafePerformIO)
39+import Foreign.ForeignPtr (newForeignPtr_)
40+import Foreign.Ptr (nullPtr)
3641import qualified Data.Text.Foreign as T (fromPtr)
3742import Foreign (castPtr)
3843import Data.Text.Foreign (useAsPtr)
39−import Language.Javascript.JSaddle.Classes (MakeString(..))
40−import System.IO.Unsafe (unsafePerformIO)
41−
42−-- | If we already have a JSStringRef we are fine
43−instance MakeString JSString where
44− makeString = id
45− {-# INLINE makeString #-}
46−
47−instance MakeString Text where
48− makeString = textToStr
49− {-# INLINE makeString #-}
50−
51−instance MakeString String where
52− makeString = textToStr . T.pack
53− {-# INLINE makeString #-}
44+#endif
45+import Language.Javascript.JSaddle.Classes (ToJSString(..))
5446
5547-- | Convert a JavaScript string to a Haskell 'Text'
5648strToText :: MonadIO m => JSString -> m Text
5749#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
5850strToText = return . textFromJSString
5951#else
60−strToText jsstring = liftIO $ do
52+strToText jsstring' = liftIO $ withJSString jsstring' $ \jsstring -> do
6153 l <- jsstringgetlength jsstring
6254 p <- jsstringgetcharactersptr jsstring
6355 T.fromPtr (castPtr p) (fromIntegral l)
@@ -71,9 +63,14 @@ textToStr = textToJSString
7163#else
7264textToStr text = unsafePerformIO $
7365 useAsPtr text $ \p l ->
74− jsstringcreatewithcharacters (castPtr p) (fromIntegral l)
66+ jsstringcreatewithcharacters (castPtr p) (fromIntegral l) >>= makeNewJSString
7567#endif
7668{-# INLINE textToStr #-}
7769
78−
70+nullJSString :: JSString
71+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
72+nullJSString = pFromJSVal nullRef
73+#else
74+nullJSString = unsafePerformIO $ newForeignPtr_ nullPtr
75+#endif
7976
src/Language/Javascript/JSaddle/Types.hs+30−12
@@ -13,25 +13,30 @@
1313-----------------------------------------------------------------------------
1414
1515module Language.Javascript.JSaddle.Types (
16− JSVal(..)
17− , MutableJSArray(..)
16+ JSVal
17+ , MutableJSArray
1818 , Object(..)
19− , JSPropertyNameArray(..)
20− , JSPropertyAttributes(..)
21− , JSContextRef(..)
22− , JSString(..)
23− , Index(..)
19+ , JSPropertyNameArray
20+ , JSPropertyAttributes
21+ , JSContextRef
22+ , JSString
23+ , Index
24+ , Nullable(..)
25+ , JSM
2426) where
2527
28+import Control.Monad.Trans.Reader (ReaderT(..))
2629#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
2730import GHCJS.Types
2831import JavaScript.Object.Internal (Object(..))
2932import JavaScript.Array (MutableJSArray)
3033import Data.Word (Word(..))
34+import GHCJS.Nullable (Nullable(..))
3135#else
36+import Foreign.ForeignPtr (ForeignPtr)
3237import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
33− (JSValueRef, JSValueRefRef, JSObjectRef, JSStringRef,
34− JSContextRef, JSPropertyNameArrayRef)
38+ (OpaqueJSString, JSValueRefRef,
39+ JSContextRef, JSPropertyNameArrayRef, OpaqueJSValue)
3540import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef (JSPropertyAttributes)
3641import Foreign.C (CUInt(..))
3742#endif
@@ -42,10 +47,23 @@ type JSPropertyAttributes = Word
4247type JSContextRef = ()
4348type Index = Int
4449#else
45−type JSVal = JSValueRef
50+type JSVal = ForeignPtr OpaqueJSValue
4651type MutableJSArray = JSValueRefRef
4752type JSPropertyNameArray = JSPropertyNameArrayRef
4853type Index = CUInt
49−newtype Object = Object JSObjectRef
50−type JSString = JSStringRef
54+newtype Object = Object (ForeignPtr OpaqueJSValue)
55+type JSString = ForeignPtr OpaqueJSString
56+newtype Nullable a = Nullable a
5157#endif
58+
59+-- | The @JSM@ monad keeps track of the JavaScript context.
60+--
61+-- Given a @JSM@ function and a 'JSContextRef' you can run the
62+-- function like this...
63+--
64+-- > runReaderT jsmFunction javaScriptContext
65+--
66+-- For an example of how to set up WebKitGTK+ see tests/TestJSaddle.hs
67+type JSM = ReaderT JSContextRef IO
68+
69+
src/Language/Javascript/JSaddle/Value.hs+252−121
@@ -6,6 +6,7 @@
66#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
77{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}
88#endif
9+{-# OPTIONS_GHC -fno-warn-orphans #-}
910-----------------------------------------------------------------------------
1011--
1112-- Module : Language.Javascript.JSaddle.Value
@@ -33,12 +34,12 @@
3334module Language.Javascript.JSaddle.Value (
3435 -- * JavaScript value references
3536 JSVal
36− , MakeVal(..)
37+ , ToJSVal(..)
3738
3839 -- * Haskell types for JavaScript values
3940 , JSNull(..)
40− , JSUndefined(..)
41− , JSString(..)
41+ , JSUndefined
42+ , JSString
4243 , JSValue(..)
4344
4445 -- * Converting JavaScript values
@@ -56,51 +57,53 @@ module Language.Javascript.JSaddle.Value (
5657 , valMakeUndefined
5758 , valIsUndefined
5859 , maybeNullOrUndefined
60+ , maybeNullOrUndefined'
5961 , valMakeBool
6062 , valMakeNumber
6163 , valMakeString
6264
63− -- * Conver to and from JSValue
65+ -- * Convert to and from JSValue
6466 , deRefVal
6567 , valMakeRef
68+ , strictEqual
69+ , instanceOf
6670) where
6771
6872import Prelude hiding (catch)
6973import Language.Javascript.JSaddle.Types
70− (MutableJSArray, Object(..), JSString, JSVal(..))
74+ (Object(..), JSString, JSVal)
7175#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
76+import Language.Javascript.JSaddle.Types
77+ (MutableJSArray)
7278import GHCJS.Types (JSVal(..), isNull, isUndefined)
7379import GHCJS.Foreign (toJSBool, isTruthy, jsNull, jsUndefined)
74−import GHCJS.Marshal (toJSVal)
80+import qualified GHCJS.Marshal as GHCJS (toJSVal)
7581import GHCJS.Marshal.Pure (pToJSVal)
7682import Data.JSString.Text (textToJSString)
7783#else
7884import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
79− (jsvaluecreatejsonstring, JSType(..), jsvaluegettype,
80− jsvaluemakestring, jsvaluemakenumber, jsvaluemakeboolean,
81− jsvaluemakeundefined, jsvaluemakenull, jsvaluetoobject,
82− jsvaluetostringcopy, jsvaluetonumber, jsvaluetoboolean,
83− jsvalueisnull, jsvalueisundefined)
84−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSString
85− (jsstringgetcharactersptr, jsstringgetlength)
86−import System.IO.Unsafe (unsafePerformIO)
85+ (jsvalueisinstanceofconstructor, jsvaluecreatejsonstring,
86+ JSType(..), jsvaluegettype, jsvaluemakestring, jsvaluemakenumber,
87+ jsvaluemakeboolean, jsvaluemakeundefined, jsvaluemakenull,
88+ jsvaluetoobject, jsvaluetostringcopy, jsvaluetonumber,
89+ jsvaluetoboolean, jsvalueisnull, jsvalueisundefined,
90+ jsvalueisstrictequal)
91+import Language.Javascript.JSaddle.Native
92+ (makeNewJSVal, wrapJSString, withJSVal, withObject, withJSString,
93+ withToJSVal)
8794#endif
88−import Language.Javascript.JSaddle.Monad (JSM, catchval)
95+import Language.Javascript.JSaddle.Monad (JSM)
8996import Language.Javascript.JSaddle.Exception (rethrow)
9097import Control.Monad.Trans.Reader (ask)
91−import Control.Monad.IO.Class (MonadIO, MonadIO(..))
92−import qualified Data.Text.Foreign as T (fromPtr)
93−import Foreign (castPtr)
94−import Data.Text.Foreign (useAsPtr)
95−import Control.Applicative ((<$>))
98+import Control.Monad.IO.Class (MonadIO(..))
9699import Data.Text (Text)
97100import qualified Data.Text as T (pack)
98101import Language.Javascript.JSaddle.Classes
99− (MakeObject(..), MakeString(..), MakeVal(..),
100− MakeArgs(..))
102+ (MakeObject(..), ToJSString(..), ToJSVal(..))
101103import Language.Javascript.JSaddle.String (strToText, textToStr)
102−import Language.Javascript.JSaddle.Arguments ()
103−import Data.Word (Word)
104+import Language.Javascript.JSaddle.Arguments (MakeArgs(..))
105+import Data.Word (Word32, Word64)
106+import Data.Int (Int32, Int64)
104107
105108data JSNull = JSNull -- ^ Type that represents a value that can only be null.
106109 -- Haskell of course has no null so we are adding this type.
@@ -140,14 +143,14 @@ data JSValue = ValNull -- ^ null
140143-- false
141144-- >>> testJSaddle $ valToBool "1"
142145-- true
143−valToBool :: MakeVal val => val -> JSM Bool
146+valToBool :: ToJSVal value => value -> JSM Bool
144147#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
145−valToBool val = isTruthy <$> makeVal val
148+valToBool value = isTruthy <$> toJSVal value
146149#else
147−valToBool val = do
150+valToBool value = do
148151 gctxt <- ask
149− rval <- makeVal val
150− liftIO $ jsvaluetoboolean gctxt rval
152+ withToJSVal value $ \rval ->
153+ liftIO $ jsvaluetoboolean gctxt rval
151154#endif
152155{-# INLINE valToBool #-}
153156
@@ -170,16 +173,16 @@ valToBool val = do
170173-- 0.0
171174-- >>> testJSaddle $ show <$> valToNumber "1"
172175-- 1.0
173−valToNumber :: MakeVal val => val -> JSM Double
176+valToNumber :: ToJSVal value => value -> JSM Double
174177#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
175−valToNumber val = jsrefToNumber <$> makeVal val
178+valToNumber value = jsrefToNumber <$> toJSVal value
176179{-# INLINE valToNumber #-}
177180foreign import javascript unsafe "$r = Number($1);" jsrefToNumber :: JSVal -> Double
178181#elif defined(USE_WEBKIT)
179−valToNumber val = do
182+valToNumber value = do
180183 gctxt <- ask
181− rval <- makeVal val
182− rethrow $ liftIO . jsvaluetonumber gctxt rval
184+ withToJSVal value $ \rval ->
185+ rethrow $ liftIO . jsvaluetonumber gctxt rval
183186{-# INLINE valToNumber #-}
184187#else
185188valToNumber = undefined
@@ -204,16 +207,16 @@ valToNumber = undefined
204207--
205208-- >>> testJSaddle $ valToStr "1" >>= strToText
206209-- 1
207−valToStr :: MakeVal val => val -> JSM JSString
210+valToStr :: ToJSVal value => value -> JSM JSString
208211#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
209−valToStr val = jsrefToString <$> makeVal val
212+valToStr value = jsrefToString <$> toJSVal value
210213{-# INLINE valToStr #-}
211214foreign import javascript unsafe "$r = $1.toString();" jsrefToString :: JSVal -> JSString
212215#elif defined(USE_WEBKIT)
213−valToStr val = do
216+valToStr value = do
214217 gctxt <- ask
215− rval <- makeVal val
216− rethrow $ liftIO . jsvaluetostringcopy gctxt rval
218+ withToJSVal value $ \rval ->
219+ (rethrow $ liftIO . jsvaluetostringcopy gctxt rval) >>= wrapJSString
217220{-# INLINE valToStr #-}
218221#else
219222valToStr = undefined
@@ -238,7 +241,7 @@ valToStr = undefined
238241-- ""
239242-- >>> testJSaddle $ show <$> valToText "1"
240243-- "1"
241−valToText :: MakeVal val => val -> JSM Text
244+valToText :: ToJSVal value => value -> JSM Text
242245valToText jsvar = valToStr jsvar >>= strToText
243246{-# INLINE valToText #-}
244247
@@ -263,16 +266,16 @@ valToText jsvar = valToStr jsvar >>= strToText
263266-- "1"
264267-- >>> testJSaddle $ obj >>= valToJSON 0 >>= strToText
265268-- {}
266−valToJSON :: MakeVal val => Word -> val -> JSM JSString
269+valToJSON :: ToJSVal value => Word -> value -> JSM JSString
267270#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
268−valToJSON indent val = jsrefToJSON <$> makeVal val
271+valToJSON indent value = jsrefToJSON <$> toJSVal value
269272{-# INLINE valToJSON #-}
270273foreign import javascript unsafe "$r = JSON.stringify($1);" jsrefToJSON :: JSVal -> JSString
271274#elif defined(USE_WEBKIT)
272−valToJSON indent val = do
275+valToJSON indent value = do
273276 gctxt <- ask
274− rval <- makeVal val
275− rethrow $ liftIO . jsvaluecreatejsonstring gctxt rval (fromIntegral indent)
277+ withToJSVal value $ \rval ->
278+ (rethrow $ liftIO . jsvaluecreatejsonstring gctxt rval (fromIntegral indent)) >>= wrapJSString
276279{-# INLINE valToJSON #-}
277280#else
278281valToJSON = undefined
@@ -297,14 +300,14 @@ valToJSON = undefined
297300--
298301-- >>> testJSaddle $ valToObject "1"
299302-- 1
300−valToObject :: MakeVal val => val -> JSM Object
301−valToObject val = Object <$>
303+valToObject :: ToJSVal value => value -> JSM Object
304+valToObject value = Object <$>
302305#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
303− makeVal val
306+ toJSVal value
304307#else
305308 do gctxt <- ask
306− rval <- makeVal val
307− rethrow $ liftIO . jsvaluetoobject gctxt rval
309+ withToJSVal value $ \rval ->
310+ (rethrow $ liftIO . jsvaluetoobject gctxt rval) >>= makeNewJSVal
308311#endif
309312{-# INLINE valToObject #-}
310313
@@ -312,17 +315,17 @@ instance MakeObject JSVal where
312315 makeObject = valToObject
313316 {-# INLINE makeObject #-}
314317
315−-- | Convert to a JavaScript value (just an alias for 'makeVal')
316−val :: MakeVal value
318+-- | Convert to a JavaScript value (just an alias for 'toJSVal')
319+val :: ToJSVal value
317320 => value -- ^ value to convert to a JavaScript value
318321 -> JSM JSVal
319−val = makeVal
322+val = toJSVal
320323{-# INLINE val #-}
321324
322325-- | If we already have a JSVal we are fine
323−instance MakeVal JSVal where
324− makeVal = return
325− {-# INLINE makeVal #-}
326+instance ToJSVal JSVal where
327+ toJSVal = return
328+ {-# INLINE toJSVal #-}
326329
327330-- | A single JSVal can be used as the argument list
328331instance MakeArgs JSVal where
@@ -331,9 +334,9 @@ instance MakeArgs JSVal where
331334
332335-- | JSVal can be made by evaluating a function in 'JSM' as long
333336-- as it returns something we can make into a JSVal.
334−instance MakeVal v => MakeVal (JSM v) where
335− makeVal v = v >>= makeVal
336− {-# INLINE makeVal #-}
337+instance ToJSVal v => ToJSVal (JSM v) where
338+ toJSVal v = v >>= toJSVal
339+ {-# INLINE toJSVal #-}
337340
338341----------- null ---------------
339342-- | Make a @null@ JavaScript value
@@ -341,29 +344,35 @@ valMakeNull :: JSM JSVal
341344#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
342345valMakeNull = return jsNull
343346#else
344−valMakeNull = ask >>= (liftIO . jsvaluemakenull)
347+valMakeNull = ask >>= (liftIO . jsvaluemakenull) >>= makeNewJSVal
345348#endif
346349{-# INLINE valMakeNull #-}
347350
348351-- | Makes a @null@ JavaScript value
349−instance MakeVal JSNull where
350− makeVal = const valMakeNull
351− {-# INLINE makeVal #-}
352+instance ToJSVal JSNull where
353+ toJSVal = const valMakeNull
354+ {-# INLINE toJSVal #-}
352355
353356-- | Makes an argument list with just a single @null@ JavaScript value
354357instance MakeArgs JSNull where
355358 makeArgs _ = valMakeNull >>= (\ref -> return [ref])
356359 {-# INLINE makeArgs #-}
357360
361+-- | Makes a JSVal or @null@ JavaScript value
362+instance ToJSVal a => ToJSVal (Maybe a) where
363+ toJSVal Nothing = valMakeNull
364+ toJSVal (Just a) = toJSVal a
365+ {-# INLINE toJSVal #-}
366+
358367-- | Test a JavaScript value to see if it is @null@
359−valIsNull :: MakeVal val => val -> JSM Bool
368+valIsNull :: ToJSVal value => value -> JSM Bool
360369#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
361−valIsNull val = isNull <$> makeVal val
370+valIsNull value = isNull <$> toJSVal value
362371#else
363−valIsNull val = do
372+valIsNull value = do
364373 gctxt <- ask
365− rval <- makeVal val
366− liftIO $ jsvalueisnull gctxt rval
374+ withToJSVal value $ \rval ->
375+ liftIO $ jsvalueisnull gctxt rval
367376#endif
368377{-# INLINE valIsNull #-}
369378
@@ -373,14 +382,14 @@ valMakeUndefined :: JSM JSVal
373382#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
374383valMakeUndefined = return jsUndefined
375384#else
376−valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined)
385+valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined) >>= makeNewJSVal
377386#endif
378387{-# INLINE valMakeUndefined #-}
379388
380389-- | Makes an @undefined@ JavaScript value
381−instance MakeVal JSUndefined where
382− makeVal = const valMakeUndefined
383− {-# INLINE makeVal #-}
390+instance ToJSVal JSUndefined where
391+ toJSVal = const valMakeUndefined
392+ {-# INLINE toJSVal #-}
384393
385394--We can't allow this if JSUndefined is () as () is no args not "(null)".
386395--Use [()] instead.
@@ -393,21 +402,21 @@ instance MakeArgs () where
393402 {-# INLINE makeArgs #-}
394403
395404-- | Test a JavaScript value to see if it is @undefined@
396−valIsUndefined :: MakeVal val => val -> JSM Bool
405+valIsUndefined :: ToJSVal value => value -> JSM Bool
397406#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
398−valIsUndefined val = isUndefined <$> makeVal val
407+valIsUndefined value = isUndefined <$> toJSVal value
399408#else
400−valIsUndefined val = do
409+valIsUndefined value = do
401410 gctxt <- ask
402− rval <- makeVal val
403− liftIO $ jsvalueisundefined gctxt rval
411+ withToJSVal value $ \rval ->
412+ liftIO $ jsvalueisundefined gctxt rval
404413#endif
405414{-# INLINE valIsUndefined #-}
406415
407416-- | Convert a JSVal to a Maybe JSVal (converting null and undefined to Nothing)
408−maybeNullOrUndefined :: MakeVal val => val -> JSM (Maybe JSVal)
409−maybeNullOrUndefined val = do
410− rval <- makeVal val
417+maybeNullOrUndefined :: ToJSVal value => value -> JSM (Maybe JSVal)
418+maybeNullOrUndefined value = do
419+ rval <- toJSVal value
411420 valIsNull rval >>= \case
412421 True -> return Nothing
413422 _ ->
@@ -416,6 +425,17 @@ maybeNullOrUndefined val = do
416425 _ -> return (Just rval)
417426{-# INLINE maybeNullOrUndefined #-}
418427
428+maybeNullOrUndefined' :: ToJSVal value => (JSVal -> JSM a) -> value -> JSM (Maybe a)
429+maybeNullOrUndefined' f value = do
430+ rval <- toJSVal value
431+ valIsNull rval >>= \case
432+ True -> return Nothing
433+ _ ->
434+ valIsUndefined rval >>= \case
435+ True -> return Nothing
436+ _ -> Just <$> f rval
437+{-# INLINE maybeNullOrUndefined' #-}
438+
419439----------- booleans ---------------
420440-- | Make a JavaScript boolean value
421441valMakeBool :: Bool -> JSM JSVal
@@ -424,14 +444,14 @@ valMakeBool b = return $ toJSBool b
424444#else
425445valMakeBool b = do
426446 gctxt <- ask
427− liftIO $ jsvaluemakeboolean gctxt b
447+ (liftIO $ jsvaluemakeboolean gctxt b) >>= makeNewJSVal
428448#endif
429449{-# INLINE valMakeBool #-}
430450
431451-- | Make a JavaScript boolean value
432−instance MakeVal Bool where
433− makeVal = valMakeBool
434− {-# INLINE makeVal #-}
452+instance ToJSVal Bool where
453+ toJSVal = valMakeBool
454+ {-# INLINE toJSVal #-}
435455
436456-- | Makes an argument list with just a single JavaScript boolean value
437457instance MakeArgs Bool where
@@ -442,50 +462,108 @@ instance MakeArgs Bool where
442462-- | Make a JavaScript number
443463valMakeNumber :: Double -> JSM JSVal
444464#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
445−valMakeNumber n = liftIO $ toJSVal n
465+valMakeNumber n = liftIO $ GHCJS.toJSVal n
446466#else
447467valMakeNumber n = do
448468 gctxt <- ask
449− liftIO $ jsvaluemakenumber gctxt n
469+ (liftIO $ jsvaluemakenumber gctxt n) >>= makeNewJSVal
450470#endif
451471{-# INLINE valMakeNumber #-}
452472
453473-- | Makes a JavaScript number
454−instance MakeVal Double where
455− makeVal = valMakeNumber
456− {-# INLINE makeVal #-}
474+instance ToJSVal Double where
475+ toJSVal = valMakeNumber
476+ {-# INLINE toJSVal #-}
477+
478+instance ToJSVal Float where
479+ toJSVal = valMakeNumber . realToFrac
480+ {-# INLINE toJSVal #-}
481+
482+instance ToJSVal Word where
483+ toJSVal = valMakeNumber . fromIntegral
484+ {-# INLINE toJSVal #-}
485+
486+instance ToJSVal Word32 where
487+ toJSVal = valMakeNumber . fromIntegral
488+ {-# INLINE toJSVal #-}
489+
490+instance ToJSVal Word64 where
491+ toJSVal = valMakeNumber . fromIntegral
492+ {-# INLINE toJSVal #-}
493+
494+instance ToJSVal Int where
495+ toJSVal = valMakeNumber . fromIntegral
496+ {-# INLINE toJSVal #-}
497+
498+instance ToJSVal Int32 where
499+ toJSVal = valMakeNumber . fromIntegral
500+ {-# INLINE toJSVal #-}
501+
502+instance ToJSVal Int64 where
503+ toJSVal = valMakeNumber . fromIntegral
504+ {-# INLINE toJSVal #-}
457505
458506-- | Makes an argument list with just a single JavaScript number
459507instance MakeArgs Double where
460508 makeArgs n = valMakeNumber n >>= (\ref -> return [ref])
461509 {-# INLINE makeArgs #-}
462510
463−----------- numbers ---------------
464−-- | Make a JavaScript string
465−valMakeString :: Text -> JSM JSVal
511+-- | Make a JavaScript string from `Text`
512+valMakeText :: Text -> JSM JSVal
513+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
514+valMakeText = return . pToJSVal . textToJSString
515+#else
516+valMakeText text = do
517+ gctxt <- ask
518+ withJSString (textToStr text) $ \s ->
519+ (liftIO $ jsvaluemakestring gctxt s) >>= makeNewJSVal
520+#endif
521+{-# INLINE valMakeText #-}
522+
523+-- | Make a JavaScript string from `JSString`
524+valMakeString :: JSString -> JSM JSVal
466525#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)
467−valMakeString = return . pToJSVal . textToJSString
526+valMakeString = return . pToJSVal
468527#else
469−valMakeString text = do
528+valMakeString str = do
470529 gctxt <- ask
471− liftIO $ jsvaluemakestring gctxt (textToStr text)
530+ withJSString str $ \s ->
531+ (liftIO $ jsvaluemakestring gctxt s) >>= makeNewJSVal
472532#endif
473533{-# INLINE valMakeString #-}
474534
475535-- | Makes a JavaScript string
476−instance MakeVal Text where
477− makeVal = valMakeString
478− {-# INLINE makeVal #-}
536+instance ToJSVal Text where
537+ toJSVal = valMakeText
538+ {-# INLINE toJSVal #-}
479539
480540-- | Makes an argument list with just a single JavaScript string
481541instance MakeArgs Text where
482− makeArgs t = valMakeString t >>= (\ref -> return [ref])
542+ makeArgs t = valMakeText t >>= (\ref -> return [ref])
483543 {-# INLINE makeArgs #-}
484544
485545-- | Makes a JavaScript string
486−instance MakeVal String where
487− makeVal = valMakeString . T.pack
488− {-# INLINE makeVal #-}
546+instance ToJSVal String where
547+ toJSVal = valMakeText . T.pack
548+ {-# INLINE toJSVal #-}
549+
550+-- | Makes a JavaScript string
551+instance ToJSVal JSString where
552+ toJSVal = valMakeString
553+ {-# INLINE toJSVal #-}
554+
555+-- | If we already have a JSString we are fine
556+instance ToJSString JSString where
557+ toJSString = id
558+ {-# INLINE toJSString #-}
559+
560+instance ToJSString Text where
561+ toJSString = textToStr
562+ {-# INLINE toJSString #-}
563+
564+instance ToJSString String where
565+ toJSString = textToStr . T.pack
566+ {-# INLINE toJSString #-}
489567
490568-- | Derefernce a value reference.
491569--
@@ -507,10 +585,10 @@ instance MakeVal String where
507585-- ValString "1"
508586-- >>> testJSaddle $ show <$> valToObject True >>= deRefVal
509587-- ValObject 0x...
510−deRefVal :: MakeVal val => val -> JSM JSValue
588+deRefVal :: ToJSVal value => value -> JSM JSValue
511589#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
512−deRefVal val = do
513− valref <- makeVal val
590+deRefVal value = do
591+ valref <- toJSVal value
514592 case (jsrefGetType valref :: Int) of
515593 0 -> return ValUndefined
516594 1 -> return ValNull
@@ -525,17 +603,17 @@ foreign import javascript unsafe "$r = ($1 === undefined)?0:\
525603 (typeof $1===\"string\")?4:\
526604 (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSVal -> Int
527605#elif defined(USE_WEBKIT)
528−deRefVal val = do
606+deRefVal value = do
529607 gctxt <- ask
530− valref <- makeVal val
531− t <- liftIO $ jsvaluegettype gctxt valref
532− case t of
533− Kjstypenull -> return ValNull
534− Kjstypeundefined -> return ValUndefined
535− Kjstypeboolean -> ValBool <$> valToBool valref
536− Kjstypenumber -> ValNumber <$> valToNumber valref
537− Kjstypestring -> ValString <$> valToText valref
538− Kjstypeobject -> ValObject <$> valToObject valref
608+ v <- toJSVal value
609+ withJSVal v $ \rval ->
610+ (liftIO $ jsvaluegettype gctxt rval) >>= \case
611+ Kjstypenull -> return ValNull
612+ Kjstypeundefined -> return ValUndefined
613+ Kjstypeboolean -> ValBool <$> valToBool v
614+ Kjstypenumber -> ValNumber <$> valToNumber v
615+ Kjstypestring -> ValString <$> valToText v
616+ Kjstypeobject -> ValObject <$> valToObject v
539617#else
540618deRefVal = undefined
541619#endif
@@ -553,19 +631,19 @@ deRefVal = undefined
553631-- >>> testJSaddle $ valMakeRef (ValString $ pack "Hello")
554632-- "Hello"
555633valMakeRef :: JSValue -> JSM JSVal
556−valMakeRef val =
557− case val of
634+valMakeRef value =
635+ case value of
558636 ValNull -> valMakeNull
559637 ValUndefined -> valMakeUndefined
560638 ValBool b -> valMakeBool b
561639 ValNumber n -> valMakeNumber n
562− ValString s -> valMakeString s
640+ ValString s -> valMakeText s
563641 ValObject (Object o) -> return o
564642
565643-- | Makes a JavaScript value from a 'JSValue' ADT.
566−instance MakeVal JSValue where
567− makeVal = valMakeRef
568− {-# INLINE makeVal #-}
644+instance ToJSVal JSValue where
645+ toJSVal = valMakeRef
646+ {-# INLINE toJSVal #-}
569647
570648-- | Makes an argument list with just a single JavaScript value from a 'JSValue' ADT.
571649instance MakeArgs JSValue where
@@ -576,3 +654,56 @@ instance MakeArgs JSValue where
576654-- makeObjectRef _ = Object <$> valMakeNull
577655-- {-# INLINE makeObjectRef #-}
578656
657+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
658+foreign import javascript unsafe
659+ "$1===$2" jsvalueisstrictequal :: JSVal -> JSVal -> Bool
660+#endif
661+
662+strictEqual :: (ToJSVal a, ToJSVal b) => a -> b -> JSM Bool
663+strictEqual a b = do
664+ aval <- toJSVal a
665+ bval <- toJSVal b
666+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
667+ return $ jsvalueisstrictequal aval bval
668+#else
669+ gctxt <- ask
670+ withJSVal aval $ \aref ->
671+ withJSVal bval $ \bref ->
672+ liftIO $ jsvalueisstrictequal gctxt aref bref
673+#endif
674+
675+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
676+foreign import javascript unsafe "\
677+ try {\
678+ $r = $1 instanceof $2\
679+ }\
680+ catch(e) {\
681+ $3[0] = e;\
682+ }"
683+ js_isInstanceOf :: JSVal -> Object -> MutableJSArray -> Bool
684+#endif
685+
686+instanceOf :: (ToJSVal value, MakeObject constructor) => value -> constructor -> JSM Bool
687+instanceOf value constructor = do
688+ v <- toJSVal value
689+ c <- makeObject constructor
690+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)
691+ rethrow $ return . js_isInstanceOf v c
692+#else
693+ gctxt <- ask
694+ withJSVal v $ \rval ->
695+ withObject c $ \c' ->
696+ rethrow $ liftIO . jsvalueisinstanceofconstructor gctxt rval c'
697+#endif
698+
699+
700+
701+
702+
703+
704+
705+
706+
707+
708+
709+
tests/TestJSaddle.hs+22−14
@@ -13,6 +13,8 @@
1313
1414module Main (
1515 main
16+ , showJSaddle
17+ , listWindowProperties
1618) where
1719
1820import Prelude hiding((!!), catch)
@@ -30,38 +32,42 @@ import Graphics.UI.Gtk.WebKit.WebView
3032 (webViewGetMainFrame, webViewNew)
3133import System.IO.Unsafe (unsafePerformIO)
3234import Control.Monad.Trans.Reader (runReaderT)
33−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase (JSContextRef)
3435import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
3536 (webFrameGetGlobalContext)
3637import Language.Javascript.JSaddle
37−import Data.Text (Text)
3838import qualified Data.Text as T
39−import Control.Applicative
4039import Control.Monad.IO.Class (MonadIO(..))
4140import Control.Monad (forM, when)
4241import System.Log.Logger (debugM)
4342import Control.Lens.Getter ((^.))
4443import Data.Monoid ((<>))
44+import Control.Concurrent.MVar (MVar)
4545
4646data TestState = TestState { jsContext :: JSContextRef, window :: Window }
4747
48+state :: MVar (Maybe TestState)
4849state = unsafePerformIO $ newMVar Nothing
49−done = unsafePerformIO $ newEmptyMVar
50+{-# NOINLINE state #-}
51+
52+done :: MVar ()
53+done = unsafePerformIO newEmptyMVar
54+{-# NOINLINE done #-}
5055
5156-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])
52−testJSaddle :: MakeVal val => JSM val -> IO ()
57+testJSaddle :: ToJSVal val => JSM val -> IO ()
5358testJSaddle = testJSaddle' False
5459
5560-- >>> showJSaddle $ eval "document.body.innerHTML = 'Test'"
56−showJSaddle :: MakeVal val => JSM val -> IO ()
61+showJSaddle :: ToJSVal val => JSM val -> IO ()
5762showJSaddle = testJSaddle' True
5863
64+debugLog :: String -> IO ()
5965debugLog = debugM "jsaddle"
6066
61−testJSaddle' :: MakeVal val => Bool -> JSM val -> IO ()
67+testJSaddle' :: ToJSVal val => Bool -> JSM val -> IO ()
6268testJSaddle' showWindow f = do
6369 debugLog "taking done"
64− tryTakeMVar done
70+ _ <- tryTakeMVar done
6571 debugLog "taking state"
6672 mbState <- takeMVar state
6773 TestState {..} <- case mbState of
@@ -69,22 +75,22 @@ testJSaddle' showWindow f = do
6975 debugLog "newState"
7076 newState <- newEmptyMVar
7177 debugLog "fork"
72− forkIO $ do
78+ _ <- forkIO $ do
7379 debugLog "initGUI"
74− initGUI
80+ _ <- initGUI
7581 debugLog "windowNew"
7682 window <- windowNew
7783 debugLog "timeoutAdd"
78− timeoutAddFull (yield >> return True) priorityLow 10
84+ _ <- timeoutAddFull (yield >> return True) priorityLow 10
7985 windowSetDefaultSize window 900 600
8086 windowSetPosition window WinPosCenter
8187 scrollWin <- scrolledWindowNew Nothing Nothing
8288 webView <- webViewNew
8389 window `containerAdd` scrollWin
8490 scrollWin `containerAdd` webView
85− on window objectDestroy $ do
91+ _ <- on window objectDestroy $ do
8692 debugLog "onDestroy"
87− tryTakeMVar state
93+ _ <- tryTakeMVar state
8894 debugLog "put state"
8995 putMVar state Nothing
9096 debugLog "mainQuit"
@@ -112,12 +118,14 @@ testJSaddle' showWindow f = do
112118 putMVar state $ Just TestState {..}
113119 return x
114120
121+main :: IO ()
115122main = do
116123 testJSaddle $ return ()
117124 Just TestState{..} <- takeMVar state
118125 postGUIAsync $ widgetDestroy window
119126 takeMVar done
120127
128+listWindowProperties :: IO ()
121129listWindowProperties = testJSaddle $ T.pack . show <$> do
122130 window <- jsg "window"
123131 names <- propertyNames window
@@ -129,7 +137,7 @@ listWindowProperties = testJSaddle $ T.pack . show <$> do
129137 \(JSException e) -> do
130138 n <- strToText name
131139 msg <- valToText e
132− return (n, (T.pack $ " error ") <> msg)
140+ return (n, T.pack " error " <> msg)
133141
134142
135143