on master help sign in
Refactoring and docs.
commit245c81cae340c90f8c6789d3e464988b7b033763 authorHamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> dateMon 28 Jan 2013, 21:25 (14 years ago) parentcb2755a
14 files changed+1091−363
jsc.cabal+30−12 src/Language/Javascript/JSC.hs+55−36 src/Language/Javascript/JSC/Arguments.hs+58 src/Language/Javascript/JSC/Classes.hs+52 src/Language/Javascript/JSC/Evaluate.hs+60 src/Language/Javascript/JSC/Exception.hs+35 src/Language/Javascript/JSC/JMacro.hs+4−3 src/Language/Javascript/JSC/Monad.hs+28−7 src/Language/Javascript/JSC/Object.hs+204−169 src/Language/Javascript/JSC/PropRef.hs+25 src/Language/Javascript/JSC/Properties.hs+154 src/Language/Javascript/JSC/String.hs+60 src/Language/Javascript/JSC/Value.hs+240−136 tests/TestJSC.hs+86
jsc.cabal+30−12
@@ -1,21 +1,39 @@
1−Name: jsc
2−Version: 0.0.1
3−Synopsis: High level interface for webkit-javascriptcore
4−Description: High level interface for JavaScriptCore module from webkitgtk
5−License: MIT
6−License-file: LICENSE
7−Author: Hamish Mackenzie
8−Maintainer: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
9−Build-Type: Custom
10−Cabal-Version: >= 1.8
1+name: jsc
2+version: 0.0.1
3+cabal-version: >=1.10
4+license: MIT
5+license-file: LICENSE
6+maintainer: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
7+synopsis: 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: ""
1113
12−Library
14+library
1315 build-depends: template-haskell -any, base -any,
1416 text >=0.11.2.3 && <0.12, transformers >=0.3.0.0 && <0.4,
1517 webkit-javascriptcore >=0.0.0.999 && <0.1, jmacro >=0.6.3 && <0.7
16− exposed-modules: Language.Javascript.JSC.JMacro
18+ exposed-modules: Language.Javascript.JSC.String
19+ Language.Javascript.JSC.Arguments Language.Javascript.JSC.Classes
20+ Language.Javascript.JSC.Properties Language.Javascript.JSC.JMacro
1721 Language.Javascript.JSC.Object Language.Javascript.JSC
1822 Language.Javascript.JSC.Value Language.Javascript.JSC.Monad
23+ Language.Javascript.JSC.Exception Language.Javascript.JSC.Evaluate
1924 exposed: True
2025 buildable: True
26+ default-language: Haskell2010
2127 hs-source-dirs: src
28+ other-modules: Language.Javascript.JSC.PropRef
29+
30+test-suite test-tool
31+ build-depends: template-haskell -any, base -any,
32+ text >=0.11.2.3 && <0.12, transformers >=0.3.0.0 && <0.4,
33+ webkit-javascriptcore >=0.0.0.999 && <0.1, jmacro >=0.6.3 && <0.7,
34+ webkit -any, gtk -any, glib -any, jsc ==0.0.1
35+ type: exitcode-stdio-1.0
36+ main-is: TestJSC.hs
37+ buildable: True
38+ default-language: Haskell2010
39+ hs-source-dirs: tests
src/Language/Javascript/JSC.hs+55−36
@@ -1,9 +1,3 @@
1−{-# LANGUAGE ForeignFunctionInterface
2− , TypeSynonymInstances
3− , FlexibleInstances
4− , DeriveDataTypeable
5− , TemplateHaskell
6− , QuasiQuotes #-}
71-----------------------------------------------------------------------------
82--
93-- Module : Language.Javascript.JSC
@@ -12,46 +6,71 @@
126--
137-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
148--
15−-- |
16−--
9+-- | This package provides an EDSL for calling JavaScript code using
10+-- the JavaScriptCore engine and low level Haskell bindings
11+-- in the webkit-javascriptcore library <https://github.com/ghcjs/webkit-javascriptcore>.
1712-----------------------------------------------------------------------------
1813
1914module Language.Javascript.JSC (
15+ -- * JSC EDSL
16+ -- | The 'JSC' monad gives us the context for evaluation. In keeping
17+ -- with JavaScript the EDSL has
18+ --
19+ -- * /Weakish typing/ - type classes are used to convert to JSValueRef
20+ -- and JSObjectRef types
21+ --
22+ -- * /Strict evaluation/ - function in the 'JSC' monad can be passed in
23+ -- place of a value and will evaluated and converted to JSValueRef or
24+ -- JSObjectRef and then passed on to JavaScript
25+ --
26+ -- JSC should be used to write wrappers for JavaScript libraries that provide
27+ -- more type safety.
28+
29+ -- * Code Examples
30+ -- | The code examples in this documentation are executed with a 'runjs'
31+ -- function that executes the example code in the JSC monad and converts
32+ -- the result to 'Text' with 'valToText'. It also catches unhandled
33+ -- exceptions with 'catch'. The source code can be found in tests/TestJSC.hs
34+ --
35+ -- Where it makes sense code examples are given in two forms. One
36+ -- that uses 'eval' to run a purely JavaScript version and one that
37+ -- uses more of the JSC EDSL feature being demonstated.
38+
39+ -- * Calling Haskell from JavaScript
40+ -- | You can call back into haskell from JavaScript using 'fun' to
41+ -- convert a Haskell function in the JSC monad into a javascript
42+ -- value.
43+
44+ -- * JMacro Support
45+ -- | If you want a more JavaScript like syntax we also have a
46+ -- JMacro based QuasiQuoter 'evalJM'. This converts your JMacro
47+ -- code into a string literal and a call to @eval@ to evaluate it
48+ -- using JavaScriptCore.
49+
50+ -- * GHCJS Support
51+ -- | Because it uses webkit-javascriptcore you can compile your JSC code to
52+ -- JavaScript using GHCJS <https://github.com/ghcjs/ghcjs> and run it
53+ -- in a web browser. Calls to the WebKitGTK+ JavaScriptCore C functions
54+ -- will be replaced with JavaScript function calls.
55+
56+ -- * Modules
2057 module Language.Javascript.JSC.Monad
58+ , module Language.Javascript.JSC.Exception
2159 , module Language.Javascript.JSC.Value
60+ , module Language.Javascript.JSC.Arguments
61+ , module Language.Javascript.JSC.Properties
2262 , module Language.Javascript.JSC.Object
63+ , module Language.Javascript.JSC.Evaluate
2364 , module Language.Javascript.JSC.JMacro
24−
25− , evaluateScript
26− , eval
65+ , module Language.Javascript.JSC.String
2766) where
2867
2968import Language.Javascript.JSC.Monad
69+import Language.Javascript.JSC.Exception
3070import Language.Javascript.JSC.Value
71+import Language.Javascript.JSC.Arguments
72+import Language.Javascript.JSC.Properties
3173import Language.Javascript.JSC.Object
74+import Language.Javascript.JSC.Evaluate
3275import Language.Javascript.JSC.JMacro
33−
34−import Control.Monad.Trans.Reader (ask)
35−import Control.Monad.IO.Class (MonadIO(..))
36−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSContextRef
37− (jscontextgetglobalobject)
38−import Foreign (nullPtr)
39−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
40− (jsevaluatescript, JSValueRefRef)
41−
42−evaluateScript :: MakeStringRef script
43− => MakeStringRef url
44− => script
45− -> JSObjectRef
46− -> url
47− -> Int
48− -> JSValueRefRef
49− -> JSC JSValueRef
50−evaluateScript script this url line exception = do
51− gctxt <- ask
52− sScript <- makeStringRef script
53− sUrl <- makeStringRef url
54− liftIO $ jsevaluatescript gctxt sScript this sUrl line exception
55−
56−eval script = rethrow $ evaluateScript script nullPtr (nullPtr::JSStringRef) 1
57−
76+import Language.Javascript.JSC.String
src/Language/Javascript/JSC/Arguments.hsnew file+58
@@ -0,0 +1,58 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE TypeSynonymInstances #-}
3+-----------------------------------------------------------------------------
4+--
5+-- Module : Language.Javascript.JSC.Arguments
6+-- Copyright : (c) Hamish Mackenzie
7+-- License : MIT
8+--
9+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.Javascript.JSC.Arguments (
16+ MakeArgRefs(..)
17+) where
18+
19+import Language.Javascript.JSC.Classes
20+ (MakeValueRef(..), MakeValueRef, MakeArgRefs(..), MakeArgRefs)
21+import Language.Javascript.JSC.Monad (JSC)
22+
23+instance MakeArgRefs arg => MakeArgRefs (JSC arg) where
24+ makeArgRefs arg = arg >>= makeArgRefs
25+
26+instance MakeValueRef arg => MakeArgRefs [arg] where
27+ makeArgRefs = mapM makeValueRef
28+
29+instance (MakeValueRef arg1, MakeValueRef arg2) => MakeArgRefs (arg1, arg2) where
30+ makeArgRefs (arg1, arg2) = do
31+ rarg1 <- makeValueRef arg1
32+ rarg2 <- makeValueRef arg2
33+ return [rarg1, rarg2]
34+
35+instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3) => MakeArgRefs (arg1, arg2, arg3) where
36+ makeArgRefs (arg1, arg2, arg3) = do
37+ rarg1 <- makeValueRef arg1
38+ rarg2 <- makeValueRef arg2
39+ rarg3 <- makeValueRef arg3
40+ return [rarg1, rarg2, rarg3]
41+
42+instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4) => MakeArgRefs (arg1, arg2, arg3, arg4) where
43+ makeArgRefs (arg1, arg2, arg3, arg4) = do
44+ rarg1 <- makeValueRef arg1
45+ rarg2 <- makeValueRef arg2
46+ rarg3 <- makeValueRef arg3
47+ rarg4 <- makeValueRef arg4
48+ return [rarg1, rarg2, rarg3, rarg4]
49+
50+instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4, MakeValueRef arg5) => MakeArgRefs (arg1, arg2, arg3, arg4, arg5) where
51+ makeArgRefs (arg1, arg2, arg3, arg4, arg5) = do
52+ rarg1 <- makeValueRef arg1
53+ rarg2 <- makeValueRef arg2
54+ rarg3 <- makeValueRef arg3
55+ rarg4 <- makeValueRef arg4
56+ rarg5 <- makeValueRef arg5
57+ return [rarg1, rarg2, rarg3, rarg4, rarg5]
58+
src/Language/Javascript/JSC/Classes.hsnew file+52
@@ -0,0 +1,52 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.Javascript.JSC.Classes
4+-- Copyright : (c) Hamish Mackenzie
5+-- License : MIT
6+--
7+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
8+--
9+-- | These classes are used to make various JavaScriptCore types
10+-- out of whatever we have. Functions in jsc take these as inputs.
11+-- This alows implicit casting and eager evaluation.
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.Javascript.JSC.Classes (
16+ -- * Type classes to convert Haskell data to JavaScript
17+ MakeValueRef(..)
18+ , MakeStringRef(..)
19+ , MakeArgRefs(..)
20+ , MakeObjectRef(..)
21+ , MakePropRef(..)
22+) where
23+
24+import Control.Monad.IO.Class (MonadIO)
25+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
26+ (JSObjectRef, JSStringRef, JSValueRef)
27+import Language.Javascript.JSC.Monad (JSC)
28+import Language.Javascript.JSC.PropRef (JSPropRef)
29+
30+-- | Anything that can be used to make a JavaScript value reference
31+class MakeValueRef a where
32+ makeValueRef :: a -> JSC JSValueRef
33+
34+-- | Anything that can be used to make a JavaScript string reference
35+class MakeStringRef a where
36+ makeStringRef :: MonadIO m => a -> m JSStringRef
37+
38+-- | Anything that can be used to make a list of JavaScript value
39+-- references for use as function arguments
40+class MakeArgRefs this where
41+ makeArgRefs :: this -> JSC [JSValueRef]
42+
43+-- | Anything that can be used to make a JavaScript object reference
44+class MakeObjectRef this where
45+ makeObjectRef :: this -> JSC JSObjectRef
46+
47+-- | Anything that can be used to make a JavaScript property reference
48+class MakePropRef this where
49+ makePropRef :: this -> JSC JSPropRef
50+
51+
52+
src/Language/Javascript/JSC/Evaluate.hsnew file+60
@@ -0,0 +1,60 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.Javascript.JSC.Evaluate
4+-- Copyright : (c) Hamish Mackenzie
5+-- License : MIT
6+--
7+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
8+--
9+-- | If you just want to run some JavaScript that you have as a string this is
10+-- you can use 'eval' or 'evaluateScript'.
11+--
12+-----------------------------------------------------------------------------
13+
14+module Language.Javascript.JSC.Evaluate (
15+ evaluateScript
16+ , eval
17+) where
18+
19+import Control.Monad.Trans.Reader (ask)
20+import Control.Monad.IO.Class (MonadIO(..))
21+import Foreign (nullPtr)
22+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
23+ (JSStringRef, JSValueRef, JSObjectRef, jsevaluatescript,
24+ JSValueRefRef)
25+import Language.Javascript.JSC.Exception (rethrow)
26+import Language.Javascript.JSC.Value ()
27+import Language.Javascript.JSC.Object ()
28+import Language.Javascript.JSC.Classes
29+ (MakeObjectRef(..), MakeStringRef(..))
30+import Language.Javascript.JSC.Monad (JSC)
31+
32+
33+-- | Evaluates a script (like eval in java script). Unlike 'eval' this function lets you
34+-- specify a source URL and starting line number for beter error information.
35+--
36+-- >>> runjs $ (evaluateScript "\n\n{" global "FileName" 53 >>= valToText) `catch` \(JSException e) -> array (e,e!"sourceURL", e!"line") >>= valToText
37+-- "SyntaxError: Expected token '}',FileName,55"
38+evaluateScript :: (MakeStringRef script, MakeObjectRef this, MakeStringRef url)
39+ => script -- ^ JavaScript to evaluate
40+ -> this
41+ -> url
42+ -> Int -- ^ The Line number of the first line of the script
43+ -> JSC JSValueRef
44+evaluateScript script this url line = do
45+ gctxt <- ask
46+ sScript <- makeStringRef script
47+ thisr <- makeObjectRef this
48+ sUrl <- makeStringRef url
49+ rethrow $ liftIO . jsevaluatescript gctxt sScript thisr sUrl line
50+
51+-- | Evaluates a script (like eval in java script)
52+--
53+-- >>> runjs $ eval "1+1"
54+-- "2"
55+eval :: MakeStringRef script
56+ => script -- ^ JavaScript to evaluate
57+ -> JSC JSValueRef
58+eval script = evaluateScript script (nullPtr::JSObjectRef) (nullPtr::JSStringRef) 1
59+
60+
src/Language/Javascript/JSC/Exception.hsnew file+35
@@ -0,0 +1,35 @@
1+{-# LANGUAGE DeriveDataTypeable #-}
2+-----------------------------------------------------------------------------
3+--
4+-- Module : Language.Javascript.JSC.Exception
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.JSC.Exception (
15+ JSException(..)
16+ , rethrow
17+) where
18+
19+import qualified Control.Exception as E (throwIO, Exception)
20+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
21+ (JSValueRefRef, JSValueRef)
22+import Data.Typeable (Typeable)
23+import Language.Javascript.JSC.Monad (catchval, JSC)
24+import Control.Monad.IO.Class (MonadIO(..))
25+import Data.Text (Text)
26+
27+data JSException = JSException JSValueRef deriving (Show, Typeable)
28+
29+instance E.Exception JSException
30+
31+-- | Catch JavaScript exceptions and rethrow Haskell ones
32+rethrow :: (JSValueRefRef -> JSC a) -> JSC a
33+rethrow f = f `catchval` \e -> do
34+ liftIO . E.throwIO $ JSException e
35+
src/Language/Javascript/JSC/JMacro.hs+4−3
@@ -7,7 +7,8 @@
77--
88-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
99--
10−-- |
10+-- | Helper function to make it easy to embed JMacro in your code and
11+-- use it from JSC
1112--
1213-----------------------------------------------------------------------------
1314
@@ -21,9 +22,9 @@ import Language.Haskell.TH
2122import Language.Javascript.JMacro (jmacroE, renderJs)
2223
2324evalJM :: QuasiQuoter
24−evalJM = QuasiQuoter {quoteExp = quoteEvalJM}
25+evalJM = jmacroE {quoteExp = quoteEvalJM}
2526
2627quoteEvalJM :: String -> ExpQ
27−quoteEvalJM s = do
28+quoteEvalJM s =
2829 appE (varE 'return) [|AppE (VarE $ mkName "Language.Javascript.JSC.eval")
2930 (LitE . StringL . show $ renderJs $(quoteExp jmacroE s))|]
src/Language/Javascript/JSC/Monad.hs+28−7
@@ -1,7 +1,3 @@
1−{-# LANGUAGE ForeignFunctionInterface
2− , TypeSynonymInstances
3− , FlexibleInstances
4− , DeriveDataTypeable #-}
51-----------------------------------------------------------------------------
62--
73-- Module : Language.Javascript.JSC.Monad
@@ -10,13 +6,17 @@
106--
117-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
128--
13−-- |
9+-- | JSC monad keeps track of the JavaScript context
1410--
1511-----------------------------------------------------------------------------
1612
1713module Language.Javascript.JSC.Monad (
14+ -- * Types
1815 JSC(..)
1916 , JSContextRef
17+
18+ -- * Exception Handling
19+ , catchval
2020 , catch
2121) where
2222
@@ -27,11 +27,32 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
2727import Control.Monad.IO.Class (MonadIO(..))
2828import Foreign (nullPtr, alloca)
2929import Foreign.Storable (Storable(..))
30+import qualified Control.Exception as E (Exception, catch)
3031
32+-- | The @JSC@ monad keeps track of the JavaScript context.
33+--
34+-- Given a @JSC@ function and a 'JSContextRef' you can run the
35+-- function like this...
36+--
37+-- > runReaderT jscFunction javaScriptContext
38+--
39+-- For an example of how to set up WebKitGTK+ see tests/TestJSC.hs
3140type JSC = ReaderT JSContextRef IO
3241
33−catch :: (JSValueRefRef -> JSC a) -> (JSValueRef -> JSC a) -> JSC a
34−catch f catcher = do
42+-- | Wrapped version of 'E.catch' that runs in a MonadIO that works
43+-- a bit better with 'JSC'
44+catch :: (MonadIO m, E.Exception e)
45+ => ReaderT r IO b
46+ -> (e -> ReaderT r IO b)
47+ -> ReaderT r m b
48+t `catch` c = do
49+ r <- ask
50+ liftIO (runReaderT t r `E.catch` \e -> runReaderT (c e) r)
51+
52+-- | Handle JavaScriptCore functions that take a JSValueRefRef in order
53+-- to throw exceptions.
54+catchval :: (JSValueRefRef -> JSC a) -> (JSValueRef -> JSC a) -> JSC a
55+catchval f catcher = do
3556 gctxt <- ask
3657 liftIO . alloca $ \pexc -> flip runReaderT gctxt $ do
3758 liftIO $ poke pexc nullPtr
src/Language/Javascript/JSC/Object.hs+204−169
@@ -1,8 +1,7 @@
1−{-# LANGUAGE ForeignFunctionInterface
2− , TypeSynonymInstances
3− , FlexibleInstances
4− , DeriveDataTypeable
5− , ScopedTypeVariables #-}
1+{-# LANGUAGE ForeignFunctionInterface #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE TypeSynonymInstances #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
65-----------------------------------------------------------------------------
76--
87-- Module : Language.Javascript.JSC.Object
@@ -11,39 +10,74 @@
1110--
1211-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
1312--
14−-- |
13+-- | Interface to JavaScript object
1514--
1615-----------------------------------------------------------------------------
1716
1817module Language.Javascript.JSC.Object (
19− objSetProperty
20− , objGetProperty
21− , objCallAsFunction
18+ JSObjectRef
19+ , MakeObjectRef(..)
20+
21+ -- * Property lookup
2222 , (!)
2323 , (!!)
24− , (#)
24+
25+ -- * Setting the value of a property
2526 , (<#)
27+
28+ -- * Calling JavaSctipt
29+ , (#)
2630 , new
2731 , call
32+
33+ -- * Calling Haskell From JavaScript
2834 , function
2935 , fun
36+ , JSCallAsFunction(..)
37+ -- ** Object Constructors
38+
39+ -- | There is no good way to support calling haskell code as a JavaScript
40+ -- constructor for the same reason that the return type of
41+ -- 'JSCallAsFunction' is 'JSUndefined'.
42+ --
43+ -- Instead of writing a constructor in Haskell write a function
44+ -- that takes a continuation. Create the JavaScript object
45+ -- and pass it to the continuation.
46+
47+ -- * Arrays
3048 , array
49+
50+ -- * Global Object
3151 , global
52+
53+ -- * Enumerating Properties
54+ , copyPropertyNames
55+ , propertyNames
56+
57+ -- * Low level
58+ , objCallAsFunction
59+ , objCallAsConstructor
3260) where
3361
3462import Prelude hiding ((!!))
3563import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
36− (JSStringRef, JSObjectRef, JSValueRefRef, JSValueRef, JSContextRef)
37−import Foreign.C (CUInt, CULong(..))
64+ (JSPropertyNameArrayRef, JSStringRef, JSObjectRef, JSValueRefRef,
65+ JSValueRef, JSContextRef)
66+import Foreign.C (CSize(..), CUInt(..), CULong(..))
3867import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
39− (jsobjectsetpropertyatindex, jsobjectgetpropertyatindex,
40− jsobjectcallasconstructor, jsobjectmakearray,
41− jsobjectcallasfunction, jsobjectgetproperty, jsobjectsetproperty,
42− JSPropertyAttributes, JSObjectCallAsFunctionCallback,
43− jsobjectmakefunctionwithcallback)
68+ (jspropertynamearraygetnameatindex, jspropertynamearraygetcount,
69+ jsobjectcopypropertynames, jsobjectsetpropertyatindex,
70+ jsobjectgetpropertyatindex, jsobjectcallasconstructor,
71+ jsobjectmakearray, jsobjectcallasfunction, jsobjectgetproperty,
72+ jsobjectsetproperty, JSPropertyAttributes,
73+ JSObjectCallAsFunctionCallback, jsobjectmakefunctionwithcallback)
74+import Language.Javascript.JSC.Exception (rethrow)
4475import Language.Javascript.JSC.Value
45− (valMakeUndefined, valToObject, MakeValueRef(..),
46− MakeStringRef(..), MakeArgRefs(..), rethrow)
76+ (JSUndefined, valMakeUndefined, valToObject)
77+import Language.Javascript.JSC.PropRef (JSPropRef(..))
78+import Language.Javascript.JSC.Classes
79+ (MakeValueRef(..), MakeStringRef(..), MakeArgRefs(..), MakePropRef(..),
80+ MakeObjectRef(..))
4781import Language.Javascript.JSC.Monad
4882 (JSC)
4983import Control.Monad.Trans.Reader (runReaderT, ask)
@@ -57,154 +91,53 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
5791 (jsvaluemakeundefined)
5892import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSContextRef
5993 (jscontextgetglobalobject)
94+import Language.Javascript.JSC.Properties
6095
61−---- Object Referene ----
62−class MakeObjectRef this where
63− makeObjectRef :: this -> JSC JSObjectRef
64−
96+-- | If we already have a JSObjectRef we are fine
6597instance MakeObjectRef JSObjectRef where
6698 makeObjectRef = return
6799
100+-- | JSObjectRef can be made by evaluating a function in 'JSC' as long
101+-- as it returns something we can make into a JSObjectRef.
68102instance MakeObjectRef v => MakeObjectRef (JSC v) where
69− makeObjectRef v = v >>= makeObjectRef
70−
71−---- Property References ----
72−data JSPropRef = JSPropRef JSObjectRef JSStringRef
73− | JSPropIndexRef JSObjectRef CUInt
74−
75−class MakePropRef this where
76− makePropRef :: this -> JSC JSPropRef
77−
78−instance MakePropRef JSPropRef where
79− makePropRef = return
80−
81−instance MakePropRef prop => MakePropRef (JSC prop) where
82− makePropRef prop = prop >>= makePropRef
83−
84−instance MakeObjectRef JSPropRef where
85− makeObjectRef = objGetProperty
86−
87−instance MakeValueRef JSPropRef where
88− makeValueRef = objGetProperty
89−
90−instance MakeArgRefs JSPropRef where
91− makeArgRefs p = do
92− rarg <- objGetProperty p
93− return [rarg]
94−
95−---- String global scope ----
96−instance MakePropRef String where
97− makePropRef name = do
98− this <- global
99− rname <- makeStringRef name
100− return (JSPropRef this rname)
101−
102−instance MakeObjectRef String where
103− makeObjectRef name = do
104− this <- global
105− rname <- makeStringRef name
106− (rethrow $ objGetPropertyByName this rname) >>= valToObject
103+ makeObjectRef v = v >>= makeObjectRef
107104
108−global :: JSC JSObjectRef
109−global = ask >>= (liftIO . jscontextgetglobalobject)
110−
111−objSetPropertyByName :: (MakeStringRef name, MakeValueRef val)
112− => JSObjectRef
113− -> name
114− -> val
115− -> JSPropertyAttributes
116− -> JSValueRefRef
117− -> JSC ()
118−objSetPropertyByName this name val attributes exceptions = do
119− gctxt <- ask
120− sName <- makeStringRef name
121− vref <- makeValueRef val
122− liftIO $ jsobjectsetproperty gctxt this sName vref attributes exceptions
123−
124−objSetPropertyAtIndex :: (MakeValueRef val)
125− => JSObjectRef
126− -> CUInt
127− -> val
128− -> JSValueRefRef
129− -> JSC ()
130−objSetPropertyAtIndex this index val exceptions = do
131− gctxt <- ask
132− vref <- makeValueRef val
133− liftIO $ jsobjectsetpropertyatindex gctxt this index vref exceptions
134−
135−objSetProperty :: (MakeValueRef val)
136− => JSPropRef
137− -> val
138− -> JSC ()
139−objSetProperty (JSPropRef this name ) val = rethrow $ objSetPropertyByName this name val 0
140−objSetProperty (JSPropIndexRef this index) val = rethrow $ objSetPropertyAtIndex this index val
141−
142−objGetPropertyByName :: MakeStringRef name
143− => JSObjectRef
144− -> name
145− -> JSValueRefRef
146− -> JSC JSValueRef
147−objGetPropertyByName this name exceptions = do
148− gctxt <- ask
149− sName <- makeStringRef name
150− liftIO $ jsobjectgetproperty gctxt this sName exceptions
151−
152−objGetPropertyAtIndex :: JSObjectRef
153− -> CUInt
154− -> JSValueRefRef
155− -> JSC JSValueRef
156−objGetPropertyAtIndex this index exceptions = do
157− gctxt <- ask
158− liftIO $ jsobjectgetpropertyatindex gctxt this index exceptions
159−
160−objGetProperty :: JSPropRef
161− -> JSC JSValueRef
162−objGetProperty (JSPropRef this name ) = rethrow $ objGetPropertyByName this name
163−objGetProperty (JSPropIndexRef this index) = rethrow $ objGetPropertyAtIndex this index
164−
165−objGetProperty' :: JSPropRef
166− -> JSC (JSObjectRef, JSValueRef)
167−objGetProperty' (JSPropRef this name) = do
168− p <- rethrow $ objGetPropertyByName this name
169− return (this, p)
170−objGetProperty' (JSPropIndexRef this index) = do
171− p <- rethrow $ objGetPropertyAtIndex this index
172− return (this, p)
173−
174−objCallAsFunction :: MakeArgRefs args
175− => JSObjectRef
176− -> JSObjectRef
177− -> args
178− -> JSValueRefRef
179− -> JSC JSValueRef
180−objCallAsFunction function this args exceptions = do
181− gctxt <- ask
182− rargs <- makeArgRefs args
183− liftIO $ withArrayLen rargs $ \ largs pargs ->
184− jsobjectcallasfunction gctxt function this (fromIntegral largs) pargs exceptions
185−
186−objCallAsConstructor :: MakeArgRefs args
187− => JSObjectRef
188− -> args
189− -> JSValueRefRef
190− -> JSC JSValueRef
191−objCallAsConstructor function args exceptions = do
192− gctxt <- ask
193− rargs <- makeArgRefs args
194− liftIO $ withArrayLen rargs $ \ largs pargs ->
195− jsobjectcallasconstructor gctxt function (fromIntegral largs) pargs exceptions
196−
197−(!) :: (MakeObjectRef this, MakeStringRef name) => this -> name -> JSC JSPropRef
105+-- | Lookup a property based on its name. This function just constructs a JSPropRef
106+-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared
107+-- to JavaScript's @.@ operator.
108+--
109+-- >>> runjs $ eval "'Hello World'.length"
110+-- >>> runjs $ val "Hello World" ! "length"
111+-- "11"
112+(!) :: (MakeObjectRef this, MakeStringRef name)
113+ => this -- ^ Object to look on
114+ -> name -- ^ Name of the property to find
115+ -> JSC JSPropRef -- ^ Property reference
198116this ! name = do
199117 rthis <- makeObjectRef this
200118 rname <- makeStringRef name
201119 return (JSPropRef rthis rname)
202120
203−(!!) :: (MakeObjectRef this) => this -> CUInt -> JSC JSPropRef
121+-- | Lookup a property based on its index. This function just constructs a JSPropRef
122+-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared
123+-- to JavaScript's @[]@ operator.
124+--
125+-- >>> runjs $ eval "'Hello World'[6]"
126+-- >>> runjs $ val "Hello World" !! 6
127+-- "W"
128+(!!) :: (MakeObjectRef this)
129+ => this -- ^ Object to look on
130+ -> CUInt -- ^ Index of the property to lookup
131+ -> JSC JSPropRef -- ^ Property reference
204132this !! index = do
205133 rthis <- makeObjectRef this
206134 return (JSPropIndexRef rthis index)
207135
136+-- | Call a JavaScript function
137+--
138+-- >>> runjs $ eval "'Hello World'.indexOf('World')"
139+-- >>> runjs $ val "Hello World" ! "indexOf" # ["World"]
140+-- "6"
208141(#) :: (MakePropRef prop, MakeArgRefs args)
209142 => prop -> args -> JSC JSValueRef
210143prop # args = do
@@ -212,20 +145,38 @@ prop # args = do
212145 (this, f) <- objGetProperty' rprop
213146 rethrow $ objCallAsFunction f this args
214147
148+-- | Call a JavaScript function
149+--
150+-- >>> runjs $ eval "var j = {}; j.x = 1; j.x"
151+-- >>> runjs $ do {j <- eval "({})"; j!"x" <# 1; j!"x"}
152+-- "1"
215153infixr 0 <#
216154(<#) :: (MakePropRef prop, MakeValueRef val)
217− => prop -> val -> JSC JSPropRef
155+ => prop -- ^ Property to set
156+ -> val -- ^ Value to set it to
157+ -> JSC JSPropRef -- ^ Reference to the property set
218158prop <# val = do
219159 p <- makePropRef prop
220160 objSetProperty p val
221161 return p
222162
163+-- | Use this to create a new JavaScript object
164+--
165+-- >>> runjs $ new "Date" (2013, 1, 1)
166+-- "Fri Feb 01 2013 00:00:00 GMT+1300 (NZDT)"
223167new :: (MakeObjectRef constructor, MakeArgRefs args)
224− => constructor -> args -> JSC JSValueRef
168+ => constructor
169+ -> args
170+ -> JSC JSValueRef
225171new constructor args = do
226172 f <- makeObjectRef constructor
227173 rethrow $ objCallAsConstructor f args
228174
175+-- | Call function with a given @this@. In most cases you should use '#'.
176+--
177+-- >>> runjs $ eval "(function(){return this;}).apply('Hello', [])"
178+-- >>> runjs $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }
179+-- "Hello"
229180call :: (MakeObjectRef function, MakeObjectRef this, MakeArgRefs args)
230181 => function -> this -> args -> JSC JSValueRef
231182call function this args = do
@@ -242,15 +193,35 @@ type JSObjectCallAsFunctionCallback' =
242193 -> JSValueRefRef
243194 -> IO JSValueRef
244195
245−type JSCallAsFunction = JSValueRef -> JSValueRef -> [JSValueRef] -> JSC ()
196+-- | Type used for Haskell functions called from JavaScript.
197+type JSCallAsFunction = JSValueRef -- ^ Function object
198+ -> JSValueRef -- ^ this
199+ -> [JSValueRef] -- ^ Function arguments
200+ -> JSC JSUndefined -- ^ Only 'JSUndefined' can be returned because
201+ -- the function may need to be executed in a
202+ -- different thread. If you need to get a
203+ -- value out pass in a continuation function
204+ -- as an argument and invoke it from haskell.
205+
206+-- | Short hand @::JSCallAsFunction@ so a haskell function can be passed to
207+-- a to a JavaScipt one.
208+--
209+-- >>> runjs $ eval "(function(f) {f('Hello');})(function (a) {console.log(a)})"
210+-- >>> runjs $ call (eval "(function(f) {f('Hello');})") global [fun $ \ _ _ args -> valToText (head args) >>= (liftIO . print) ]
211+-- "Hello"
212+-- "undefined"
213+fun :: JSCallAsFunction -> JSCallAsFunction
214+fun = id
246215
247216foreign import ccall "wrapper"
248217 mkJSObjectCallAsFunctionCallback :: JSObjectCallAsFunctionCallback' -> IO JSObjectCallAsFunctionCallback
249218
219+-- ^ Make a JavaScript function object that wraps a Haskell function.
250220function :: MakeStringRef name
251− => name
252− -> JSCallAsFunction
253− -> JSC JSObjectRef
221+ => name -- ^ Name of the function
222+ -> JSCallAsFunction -- ^ Haskell function to call
223+ -> JSC JSObjectRef -- ^ Returns a JavaScript function object that will
224+ -- call the Haskell one when it is called
254225function name f = do
255226 gctxt <- ask
256227 callback <- liftIO $ mkJSObjectCallAsFunctionCallback wrap
@@ -259,18 +230,18 @@ function name f = do
259230 where
260231 wrap ctx fobj this argc argv exception = do
261232 args <- peekArray (fromIntegral argc) argv
262− flip runReaderT ctx $ do
233+ (`runReaderT` ctx) $
263234 f fobj this args >>= makeValueRef
264235 `E.catch` \(e :: SomeException) -> do
265236 str <- runReaderT (makeValueRef . T.pack $ show e) ctx
266237 poke exception str
267238 jsvaluemakeundefined ctx
268239
269−fun :: JSCallAsFunction -> JSCallAsFunction
270−fun f = f
271−
240+-- | A callback to Haskell can be used as a JavaScript value. This will create
241+-- an anonymous JavaScript function object. Use 'function' to create one with
242+-- a name.
272243instance MakeValueRef JSCallAsFunction where
273− makeValueRef f = function (nullPtr::JSStringRef) f
244+ makeValueRef = function (nullPtr::JSStringRef)
274245
275246instance MakeArgRefs JSCallAsFunction where
276247 makeArgRefs f = do
@@ -284,25 +255,89 @@ makeArray args exceptions = do
284255 liftIO $ withArrayLen rargs $ \ len ptr ->
285256 jsobjectmakearray gctxt (fromIntegral len) ptr exceptions
286257
258+-- | Make an JavaScript array from a list of values
259+--
260+-- >>> runjs $ eval "['Hello', 'World'][1]"
261+-- >>> runjs $ array ["Hello", "World"] !! 1
262+-- "World"
263+-- >>> runjs $ eval "['Hello', null, undefined, true, 1]"
264+-- >>> runjs $ array ("Hello", JSNull, (), True, 1.0::Double)
265+-- "Hello,,,true,1"
287266array :: MakeArgRefs args => args -> JSC JSObjectRef
288267array = rethrow . makeArray
289268
269+-- | JavaScript's global object
270+global :: JSC JSObjectRef
271+global = ask >>= (liftIO . jscontextgetglobalobject)
290272
273+---- String global scope ----
274+-- | A string on its own is assumed to be the name of a property in the global object
275+instance MakePropRef String where
276+ makePropRef name = do
277+ this <- global
278+ rname <- makeStringRef name
279+ return (JSPropRef this rname)
291280
281+-- | A string on its own is assumed to be the name of a property in the global object
282+instance MakeObjectRef String where
283+ makeObjectRef name = do
284+ this <- global
285+ rname <- makeStringRef name
286+ rethrow (objGetPropertyByName this rname) >>= valToObject
292287
288+-- | Get an array containing the property names present on a given object
289+copyPropertyNames :: MakeObjectRef this => this -> JSC JSPropertyNameArrayRef
290+copyPropertyNames this = do
291+ gctxt <- ask
292+ rthis <- makeObjectRef this
293+ liftIO $ jsobjectcopypropertynames gctxt rthis
293294
295+-- | Get the number of names in a property name array
296+propertyNamesCount :: MonadIO m => JSPropertyNameArrayRef -> m CSize
297+propertyNamesCount names = liftIO $ jspropertynamearraygetcount names
294298
299+-- | Get a name out of a property name array
300+propertyNamesAt :: MonadIO m => JSPropertyNameArrayRef -> CSize -> m JSStringRef
301+propertyNamesAt names index = liftIO $ jspropertynamearraygetnameatindex names index
295302
303+-- | Convert property array to a list
304+propertyNamesList :: MonadIO m => JSPropertyNameArrayRef -> m [JSStringRef]
305+propertyNamesList names = do
306+ count <- propertyNamesCount names
307+ mapM (propertyNamesAt names) $ enumFromTo 0 (count - 1)
296308
309+-- | Get a list containing the property names present on a given object
310+propertyNames :: MakeObjectRef this => this -> JSC [JSStringRef]
311+propertyNames this = copyPropertyNames this >>= propertyNamesList
297312
313+-- | Get a list containing references to all the properties present on a given object
314+properties :: MakeObjectRef this => this -> JSC [JSPropRef]
315+properties this = propertyNames this >>= mapM (this !)
298316
317+-- | Call a JavaScript object as function. Consider using '#'.
318+objCallAsFunction :: MakeArgRefs args
319+ => JSObjectRef
320+ -> JSObjectRef
321+ -> args
322+ -> JSValueRefRef
323+ -> JSC JSValueRef
324+objCallAsFunction function this args exceptions = do
325+ gctxt <- ask
326+ rargs <- makeArgRefs args
327+ liftIO $ withArrayLen rargs $ \ largs pargs ->
328+ jsobjectcallasfunction gctxt function this (fromIntegral largs) pargs exceptions
299329
300−
301−
302−
303−
304−
305−
330+-- | Call a JavaScript object as a constructor. Consider using 'new'.
331+objCallAsConstructor :: MakeArgRefs args
332+ => JSObjectRef
333+ -> args
334+ -> JSValueRefRef
335+ -> JSC JSValueRef
336+objCallAsConstructor function args exceptions = do
337+ gctxt <- ask
338+ rargs <- makeArgRefs args
339+ liftIO $ withArrayLen rargs $ \ largs pargs ->
340+ jsobjectcallasconstructor gctxt function (fromIntegral largs) pargs exceptions
306341
307342
308343
src/Language/Javascript/JSC/PropRef.hsnew file+25
@@ -0,0 +1,25 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.Javascript.JSC.PropRef
4+-- Copyright : (c) Hamish Mackenzie
5+-- License : MIT
6+--
7+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
8+--
9+-- | Implements a reference to a property
10+--
11+-----------------------------------------------------------------------------
12+
13+module Language.Javascript.JSC.PropRef (
14+ JSPropRef(..)
15+) where
16+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
17+ (JSStringRef, JSObjectRef)
18+import Foreign.C (CUInt)
19+
20+-- | A reference to a property.
21+-- Implemented as a reference to an object and something to find the property.
22+data JSPropRef = JSPropRef JSObjectRef JSStringRef -- ^ Object and property name.
23+ | JSPropIndexRef JSObjectRef CUInt -- ^ Object and property index.
24+
25+
src/Language/Javascript/JSC/Properties.hsnew file+154
@@ -0,0 +1,154 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE TypeSynonymInstances #-}
3+-----------------------------------------------------------------------------
4+--
5+-- Module : Language.Javascript.JSC.Properties
6+-- Copyright : (c) Hamish Mackenzie
7+-- License : MIT
8+--
9+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
10+--
11+-- | Low level JavaScript object property access. In most cases you
12+-- should use "Language.Javascript.JSC.Object" instead.
13+--
14+-- This module is mostly here to implement functions needed to use
15+-- 'JSPropRef'.
16+--
17+-----------------------------------------------------------------------------
18+
19+module Language.Javascript.JSC.Properties (
20+ -- * Propery Reference
21+ JSPropRef(..)
22+ , MakePropRef(..)
23+
24+ -- * Getting Property Values
25+ , objGetPropertyByName
26+ , objGetPropertyAtIndex
27+ , objGetProperty
28+ , objGetProperty'
29+ -- * Setting Property Values
30+ , objSetPropertyByName
31+ , objSetPropertyAtIndex
32+ , objSetProperty
33+) where
34+
35+import Language.Javascript.JSC.PropRef (JSPropRef(..))
36+import Language.Javascript.JSC.Classes
37+ (MakePropRef(..), MakeObjectRef(..), MakeValueRef(..),
38+ MakeArgRefs(..), MakeStringRef(..))
39+import Language.Javascript.JSC.Monad (JSC)
40+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
41+ (JSValueRefRef, JSObjectRef)
42+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
43+ (jsobjectgetpropertyatindex, jsobjectgetproperty,
44+ jsobjectsetpropertyatindex, jsobjectsetproperty,
45+ JSPropertyAttributes)
46+import Control.Monad.Trans.Reader (ask)
47+import Control.Monad.IO.Class (MonadIO(..))
48+import Foreign.C (CUInt)
49+import Language.Javascript.JSC.Exception (rethrow)
50+import Language.Javascript.JSC.Value (JSValueRef)
51+import Language.Javascript.JSC.Arguments ()
52+import Language.Javascript.JSC.String ()
53+
54+-- | If we already have a JSPropRef we are fine
55+instance MakePropRef JSPropRef where
56+ makePropRef = return
57+
58+-- | JSPropRef can be made by evaluating a function in 'JSC' as long
59+-- as it returns something we can make into a JSPropRef.
60+instance MakePropRef prop => MakePropRef (JSC prop) where
61+ makePropRef prop = prop >>= makePropRef
62+
63+-- | We can use a property as an object.
64+instance MakeObjectRef JSPropRef where
65+ makeObjectRef = objGetProperty
66+
67+-- | We can use a property as a value.
68+instance MakeValueRef JSPropRef where
69+ makeValueRef = objGetProperty
70+
71+-- | We can pass a property as the only paramter to a function.
72+instance MakeArgRefs JSPropRef where
73+ makeArgRefs p = do
74+ rarg <- objGetProperty p
75+ return [rarg]
76+
77+-- | Get a property value given the object and the name of the property.
78+objGetPropertyByName :: MakeStringRef name
79+ => JSObjectRef -- ^ object to find the property on.
80+ -> name -- ^ name of the property.
81+ -> JSValueRefRef -- ^ exception if one is raised.
82+ -> JSC JSValueRef -- ^ returns the property value.
83+objGetPropertyByName this name exceptions = do
84+ gctxt <- ask
85+ sName <- makeStringRef name
86+ liftIO $ jsobjectgetproperty gctxt this sName exceptions
87+
88+-- | Get a property value given the object and the index of the property.
89+objGetPropertyAtIndex :: JSObjectRef -- ^ object to find the property on.
90+ -> CUInt -- ^ index of the property.
91+ -> JSValueRefRef -- ^ exception if one is raised.
92+ -> JSC JSValueRef -- ^ returns the property value.
93+objGetPropertyAtIndex this index exceptions = do
94+ gctxt <- ask
95+ liftIO $ jsobjectgetpropertyatindex gctxt this index exceptions
96+
97+-- | Gets the value of a property given a 'JSPropRef'.
98+objGetProperty :: JSPropRef -- ^ property reference.
99+ -> JSC JSValueRef -- ^ returns the property value.
100+objGetProperty (JSPropRef this name ) =
101+ rethrow $ objGetPropertyByName this name
102+objGetProperty (JSPropIndexRef this index) =
103+ rethrow $ objGetPropertyAtIndex this index
104+
105+-- | This version of 'objGetProperty' is handy when you also need to perform.
106+-- another operation on the object the property is on.
107+objGetProperty' :: JSPropRef -- ^ property reference.
108+ -> JSC (JSObjectRef, JSValueRef) -- ^ returns the object and property value.
109+objGetProperty' (JSPropRef this name) = do
110+ p <- rethrow $ objGetPropertyByName this name
111+ return (this, p)
112+objGetProperty' (JSPropIndexRef this index) = do
113+ p <- rethrow $ objGetPropertyAtIndex this index
114+ return (this, p)
115+
116+
117+-- | Set a property value given the object and the name of the property.
118+objSetPropertyByName :: (MakeStringRef name, MakeValueRef val)
119+ => JSObjectRef -- ^ object to set the property on.
120+ -> name -- ^ name of the property.
121+ -> val -- ^ new value to set the property to.
122+ -> JSPropertyAttributes -- ^ property attributes to give the property.
123+ -> JSValueRefRef -- ^ exception if one is raised.
124+ -> JSC ()
125+objSetPropertyByName this name val attributes exceptions = do
126+ gctxt <- ask
127+ sName <- makeStringRef name
128+ vref <- makeValueRef val
129+ liftIO $ jsobjectsetproperty gctxt this sName vref attributes exceptions
130+
131+-- | Set a property value given the object and the index of the property.
132+objSetPropertyAtIndex :: (MakeValueRef val)
133+ => JSObjectRef -- ^ object to find property on.
134+ -> CUInt -- ^ index of the property.
135+ -> val -- ^ new value to set the property to.
136+ -> JSValueRefRef -- ^ exception if one is raised.
137+ -> JSC ()
138+objSetPropertyAtIndex this index val exceptions = do
139+ gctxt <- ask
140+ vref <- makeValueRef val
141+ liftIO $ jsobjectsetpropertyatindex gctxt this index vref exceptions
142+
143+-- | Sets the value of a property given a 'JSPropRef'.
144+objSetProperty :: (MakeValueRef val)
145+ => JSPropRef -- ^ property reference.
146+ -> val -- ^ new value to set the property to.
147+ -> JSC ()
148+objSetProperty (JSPropRef this name ) val = rethrow $ objSetPropertyByName this name val 0
149+objSetProperty (JSPropIndexRef this index) val = rethrow $ objSetPropertyAtIndex this index val
150+
151+
152+
153+
154+
src/Language/Javascript/JSC/String.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE TypeSynonymInstances #-}
3+-----------------------------------------------------------------------------
4+--
5+-- Module : Language.Javascript.JSC.String
6+-- Copyright : (c) Hamish Mackenzie
7+-- License : MIT
8+--
9+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
10+--
11+-- | JavaScript string conversion functions
12+--
13+-----------------------------------------------------------------------------
14+
15+module Language.Javascript.JSC.String (
16+ JSStringRef
17+ , MakeStringRef(..)
18+
19+ -- * Data.Text helpers
20+ , strToText
21+ , textToStr
22+) where
23+
24+import Data.Text (Text)
25+import qualified Data.Text as T (pack)
26+import Control.Monad.IO.Class (MonadIO(..))
27+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
28+ (jsstringcreatewithcharacters, jsstringgetcharactersptr,
29+ jsstringgetlength)
30+import qualified Data.Text.Foreign as T (fromPtr)
31+import Foreign (castPtr)
32+import Data.Text.Foreign (useAsPtr)
33+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase (JSStringRef)
34+import Language.Javascript.JSC.Classes (MakeStringRef(..))
35+
36+-- | If we already have a JSStringRef we are fine
37+instance MakeStringRef JSStringRef where
38+ makeStringRef = return
39+
40+instance MakeStringRef Text where
41+ makeStringRef = textToStr
42+
43+instance MakeStringRef String where
44+ makeStringRef = textToStr . T.pack
45+
46+-- | Convert a JavaScript string to a Haskell 'Text'
47+strToText :: MonadIO m => JSStringRef -> m Text
48+strToText jsstring = liftIO $ do
49+ l <- jsstringgetlength jsstring
50+ p <- jsstringgetcharactersptr jsstring
51+ T.fromPtr (castPtr p) (fromIntegral l)
52+
53+-- | Convert a Haskell 'Text' to a JavaScript string
54+textToStr :: MonadIO m => Text -> m JSStringRef
55+textToStr text =
56+ liftIO $ useAsPtr text $ \p l ->
57+ jsstringcreatewithcharacters (castPtr p) (fromIntegral l)
58+
59+
60+
src/Language/Javascript/JSC/Value.hs+240−136
@@ -1,7 +1,5 @@
1−{-# LANGUAGE ForeignFunctionInterface
2− , TypeSynonymInstances
3− , FlexibleInstances
4− , DeriveDataTypeable #-}
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE TypeSynonymInstances #-}
53-----------------------------------------------------------------------------
64--
75-- Module : Language.Javascript.JSC.Value
@@ -10,38 +8,43 @@
108--
119-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
1210--
13−-- |
11+-- | Deals with JavaScript values. These can be
12+--
13+-- * null
14+--
15+-- * undefined
16+--
17+-- * true | false
18+--
19+-- * a double precision floating point number
20+--
21+-- * a string
22+--
23+-- * an object
1424--
1525-----------------------------------------------------------------------------
1626
1727module Language.Javascript.JSC.Value (
18− JSNull(..)
19− , JSUndefined
20− , JSBool
21− , JSNumber
22− , JSString
23− , JSValue(..)
28+ -- * JavaScript value references
29+ JSValueRef
30+ , MakeValueRef(..)
2431
25− , JSStringRef
26− , JSObjectRef
27− , JSValueRef
32+ -- * Haskell types for JavaScript values
33+ , JSNull(..)
34+ , JSUndefined(..)
35+ , JSBool(..)
36+ , JSNumber(..)
37+ , JSString(..)
38+ , JSValue(..)
2839
40+ -- * Converting JavaScript values
2941 , valToBool
3042 , valToNumber
3143 , valToStr
3244 , valToObject
33−
34− , strToText
35− , textToStr
36−
3745 , valToText
3846
39− , rethrow
40−
41− , MakeValueRef(..)
42− , MakeStringRef(..)
43− , MakeArgRefs(..)
44−
47+ -- * Make JavaScript values from Haskell ones
4548 , val
4649 , valMakeNull
4750 , valMakeUndefined
@@ -49,6 +52,7 @@ module Language.Javascript.JSC.Value (
4952 , valMakeNumber
5053 , valMakeString
5154
55+ -- * Conver to and from JSValue
5256 , deRefVal
5357 , valMakeRef
5458) where
@@ -56,7 +60,8 @@ module Language.Javascript.JSC.Value (
5660import Prelude hiding (catch)
5761import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
5862 (JSValueRefRef, JSObjectRef, JSStringRef, JSValueRef)
59−import Language.Javascript.JSC.Monad (JSC, catch)
63+import Language.Javascript.JSC.Monad (JSC, catchval)
64+import Language.Javascript.JSC.Exception (rethrow)
6065import Control.Monad.Trans.Reader (ask)
6166import Control.Monad.IO.Class (MonadIO, MonadIO(..))
6267import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
@@ -73,191 +78,273 @@ import Data.Text.Foreign (useAsPtr)
7378import Control.Applicative ((<$>))
7479import Data.Text (Text)
7580import qualified Data.Text as T (pack)
76−import qualified Control.Exception as E (throwIO, Exception)
77−import Data.Typeable (Typeable)
78−
79−data JSNull = JSNull
80−type JSUndefined = ()
81−type JSBool = Bool
82−type JSNumber = Double
83−type JSString = Text
84−
85−data JSValue = ValNull
86− | ValUndefined
87− | ValBool JSBool
88− | ValNumber JSNumber
89− | ValString JSString
90− | ValObject JSObjectRef deriving(Show, Eq)
91−
92−valToBool :: JSValueRef -> JSC JSBool
93−valToBool jsvar = do
94− gctxt <- ask
95− liftIO $ jsvaluetoboolean gctxt jsvar
96−
97−valToNumber :: JSValueRef -> JSC JSNumber
98−valToNumber jsvar = do
81+import Language.Javascript.JSC.Classes
82+ (MakeObjectRef(..), MakeStringRef(..), MakeValueRef(..),
83+ MakeArgRefs(..))
84+import Language.Javascript.JSC.String (strToText, textToStr)
85+import Language.Javascript.JSC.Arguments ()
86+
87+data JSNull = JSNull -- ^ Type that represents a value that can only be null.
88+ -- Haskell of course has no null so we are adding this type.
89+type JSUndefined = () -- ^ A type that can only be undefined in JavaScript. Using ()
90+ -- because functions in JavaScript that have no return, impicitly
91+ -- return undefined.
92+type JSBool = Bool -- ^ JavaScript boolean values map the 'Bool' haskell type.
93+type JSNumber = Double -- ^ A number in JavaScript maps nicely to 'Double'.
94+type JSString = Text -- ^ JavaScript strings can be represented with the Haskell 'Text' type.
95+
96+-- | An algebraic data type that can represent a JavaScript value. Any JavaScriptCore
97+-- 'JSValueRef' can be converted into this type.
98+data JSValue = ValNull -- ^ null
99+ | ValUndefined -- ^ undefined
100+ | ValBool JSBool -- ^ true or false
101+ | ValNumber JSNumber -- ^ a number
102+ | ValString JSString -- ^ a string
103+ | ValObject JSObjectRef -- ^ an object
104+ deriving(Show, Eq)
105+
106+-- | Given a JavaScript value get its boolean value.
107+-- All values in JavaScript convert to bool.
108+--
109+-- >>> runjs $ valToBool JSNull
110+-- False
111+-- >>> runjs $ valToBool ()
112+-- False
113+-- >>> runjs $ valToBool True
114+-- True
115+-- >>> runjs $ valToBool False
116+-- False
117+-- >>> runjs $ valToBool (1.0 :: Double)
118+-- True
119+-- >>> runjs $ valToBool (0.0 :: Double)
120+-- False
121+-- >>> runjs $ valToBool ""
122+-- False
123+-- >>> runjs $ valToBool "1"
124+-- True
125+valToBool :: MakeValueRef val => val -> JSC JSBool
126+valToBool val = do
99127 gctxt <- ask
100− rethrow $ liftIO . jsvaluetonumber gctxt jsvar
128+ rval <- makeValueRef val
129+ liftIO $ jsvaluetoboolean gctxt rval
101130
102−valToStr :: JSValueRef -> JSC JSStringRef
103−valToStr jsvar = do
131+-- | Given a JavaScript value get its numeric value.
132+-- May throw JSException.
133+--
134+-- >>> runjs $ valToNumber JSNull
135+-- 0.0
136+-- >>> runjs $ valToNumber ()
137+-- NaN
138+-- >>> runjs $ valToNumber True
139+-- 1.0
140+-- >>> runjs $ valToNumber False
141+-- 0.0
142+-- >>> runjs $ valToNumber (1.0 :: Double)
143+-- 0.0
144+-- >>> runjs $ valToNumber (0.0 :: Double)
145+-- 0.0
146+-- >>> runjs $ valToNumber ""
147+-- 0.0
148+-- >>> runjs $ valToNumber "1"
149+-- 1.0
150+valToNumber :: MakeValueRef val => val -> JSC JSNumber
151+valToNumber val = do
104152 gctxt <- ask
105− rethrow $ liftIO . jsvaluetostringcopy gctxt jsvar
153+ rval <- makeValueRef val
154+ rethrow $ liftIO . jsvaluetonumber gctxt rval
106155
107−valToObject :: JSValueRef -> JSC JSObjectRef
108−valToObject jsvar = do
156+-- | Given a JavaScript value get its string value (as a JavaScript string).
157+-- May throw JSException.
158+--
159+-- >>> runjs $ valToStr JSNull >>= strToText
160+-- "null"
161+-- >>> runjs $ valToStr () >>= strToText
162+-- "undefined"
163+-- >>> runjs $ valToStr True >>= strToText
164+-- "true"
165+-- >>> runjs $ valToStr False >>= strToText
166+-- "false"
167+-- >>> runjs $ valToStr (1.0 :: Double) >>= strToText
168+-- "1"
169+-- >>> runjs $ valToStr (0.0 :: Double) >>= strToText
170+-- "0"
171+-- >>> runjs $ valToStr "" >>= strToText
172+-- ""
173+-- >>> runjs $ valToStr "1" >>= strToText
174+-- "1"
175+valToStr :: MakeValueRef val => val -> JSC JSStringRef
176+valToStr val = do
109177 gctxt <- ask
110− rethrow $ liftIO . jsvaluetoobject gctxt jsvar
111−
112−strToText :: MonadIO m => JSStringRef -> m Text
113−strToText jsstring = liftIO $ do
114− l <- jsstringgetlength jsstring
115− p <- jsstringgetcharactersptr jsstring
116− T.fromPtr (castPtr p) (fromIntegral l)
178+ rval <- makeValueRef val
179+ rethrow $ liftIO . jsvaluetostringcopy gctxt rval
117180
118−textToStr :: MonadIO m => Text -> m JSStringRef
119−textToStr text = do
120− liftIO $ useAsPtr text $ \p l -> do
121− jsstringcreatewithcharacters (castPtr p) (fromIntegral l)
122−
123−valToText :: JSValueRef -> JSC Text
181+-- | Given a JavaScript value get its string value (as a Haskell 'Text').
182+-- May throw JSException.
183+--
184+-- >>> runjs $ valToText JSNull
185+-- "null"
186+-- >>> runjs $ valToText ()
187+-- "undefined"
188+-- >>> runjs $ valToText True
189+-- "true"
190+-- >>> runjs $ valToText False
191+-- "false"
192+-- >>> runjs $ valToText (1.0 :: Double)
193+-- "1"
194+-- >>> runjs $ valToText (0.0 :: Double)
195+-- "0"
196+-- >>> runjs $ valToText ""
197+-- ""
198+-- >>> runjs $ valToText "1"
199+-- "1"
200+valToText :: MakeValueRef val => val -> JSC Text
124201valToText jsvar = valToStr jsvar >>= strToText
125202
126−data JSException = JSException String JSValueRef deriving (Show, Typeable)
127−
128−instance E.Exception JSException
129−
130−rethrow :: (JSValueRefRef -> JSC a) -> JSC a
131−rethrow f = f `catch` \e -> do
132− s <- deRefVal e
133− liftIO . E.throwIO $ JSException (show s) e
134−
135−class MakeValueRef a where
136− makeValueRef :: a -> JSC JSValueRef
137−
138−class MakeStringRef a where
139− makeStringRef :: MonadIO m => a -> m JSStringRef
140−
141−---- Arguments ----
142−class MakeArgRefs this where
143− makeArgRefs :: this -> JSC [JSValueRef]
144−
145−instance MakeArgRefs arg => MakeArgRefs (JSC arg) where
146− makeArgRefs arg = arg >>= makeArgRefs
147−
148−instance MakeValueRef arg => MakeArgRefs [arg] where
149− makeArgRefs = mapM makeValueRef
150−
151−instance MakeArgRefs () where
152− makeArgRefs _ = return []
203+-- | Given a JavaScript value get its object value.
204+-- May throw JSException.
205+--
206+-- >>> runjs $ (valToObject JSNull >>= valToText) `catch` \ (JSException e) -> valToText e
207+-- "TypeError: 'null' is not an object"
208+-- >>> runjs $ (valToObject () >>= valToText) `catch` \ (JSException e) -> valToText e
209+-- "TypeError: 'undefined' is not an object"
210+-- >>> runjs $ valToObject True
211+-- "true"
212+-- >>> runjs $ valToObject False
213+-- "false"
214+-- >>> runjs $ valToObject (1.0 :: Double)
215+-- "1"
216+-- >>> runjs $ valToObject (0.0 :: Double)
217+-- "0"
218+-- >>> runjs $ valToObject ""
219+-- ""
220+-- >>> runjs $ valToObject "1"
221+-- "1"
222+valToObject :: MakeValueRef val => val -> JSC JSObjectRef
223+valToObject val = do
224+ gctxt <- ask
225+ rval <- makeValueRef val
226+ rethrow $ liftIO . jsvaluetoobject gctxt rval
153227
154−instance (MakeValueRef arg1, MakeValueRef arg2) => MakeArgRefs (arg1, arg2) where
155− makeArgRefs (arg1, arg2) = do
156− rarg1 <- makeValueRef arg1
157− rarg2 <- makeValueRef arg2
158− return [rarg1, rarg2]
159−
160−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3) => MakeArgRefs (arg1, arg2, arg3) where
161− makeArgRefs (arg1, arg2, arg3) = do
162− rarg1 <- makeValueRef arg1
163− rarg2 <- makeValueRef arg2
164− rarg3 <- makeValueRef arg3
165− return [rarg1, rarg2, rarg3]
166−
167−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4) => MakeArgRefs (arg1, arg2, arg3, arg4) where
168− makeArgRefs (arg1, arg2, arg3, arg4) = do
169− rarg1 <- makeValueRef arg1
170− rarg2 <- makeValueRef arg2
171− rarg3 <- makeValueRef arg3
172− rarg4 <- makeValueRef arg4
173− return [rarg1, rarg2, rarg3, rarg4]
174−
175−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4, MakeValueRef arg5) => MakeArgRefs (arg1, arg2, arg3, arg4, arg5) where
176− makeArgRefs (arg1, arg2, arg3, arg4, arg5) = do
177− rarg1 <- makeValueRef arg1
178− rarg2 <- makeValueRef arg2
179− rarg3 <- makeValueRef arg3
180− rarg4 <- makeValueRef arg4
181− rarg5 <- makeValueRef arg5
182− return [rarg1, rarg2, rarg3, rarg4, rarg5]
183−
184−val :: MakeValueRef v => v -> JSC JSValueRef
228+-- | Convert to a JavaScript value (just an alias for 'makeValueRef')
229+val :: MakeValueRef value
230+ => value -- ^ value to convert to a JavaScript value
231+ -> JSC JSValueRef
185232val = makeValueRef
186233
234+-- | If we already have a JSValueRef we are fine
187235instance MakeValueRef JSValueRef where
188236 makeValueRef = return
189237
238+-- | A single JSValueRef can be used as the argument list
190239instance MakeArgRefs JSValueRef where
191240 makeArgRefs arg = return [arg]
192241
242+-- | JSValueRef can be made by evaluating a function in 'JSC' as long
243+-- as it returns something we can make into a JSValueRef.
193244instance MakeValueRef v => MakeValueRef (JSC v) where
194245 makeValueRef v = v >>= makeValueRef
195246
196−instance MakeStringRef JSStringRef where
197− makeStringRef = return
198−
199−instance MakeStringRef Text where
200− makeStringRef = textToStr
201−
202−instance MakeStringRef String where
203− makeStringRef = textToStr . T.pack
204−
247+----------- null ---------------
248+-- | Make a @null@ JavaScript value
205249valMakeNull :: JSC JSValueRef
206250valMakeNull = ask >>= (liftIO . jsvaluemakenull)
207251
252+-- | Makes a @null@ JavaScript value
208253instance MakeValueRef JSNull where
209254 makeValueRef = const valMakeNull
210255
256+-- | Makes an argument list with just a single @null@ JavaScript value
211257instance MakeArgRefs JSNull where
212258 makeArgRefs _ = valMakeNull >>= (\ref -> return [ref])
213259
260+----------- undefined ---------------
261+-- | Make an @undefined@ JavaScript value
214262valMakeUndefined :: JSC JSValueRef
215263valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined)
216264
265+-- | Makes an @undefined@ JavaScript value
217266instance MakeValueRef JSUndefined where
218267 makeValueRef = const valMakeUndefined
219268
220−--We can't allow this if JSUndefined is () as () is now args not "(null)"
269+--We can't allow this if JSUndefined is () as () is no args not "(null)".
270+--Use [()] instead.
221271--instance MakeArgRefs JSUndefined where
222272-- makeArgRefs _ = valMakeUndefined >>= (\ref -> return [ref])
223273
274+-- | This allows us to pass no arguments easily (altenative would be to use @[]::[JSValueRef]@).
275+instance MakeArgRefs () where
276+ makeArgRefs _ = return []
277+
278+----------- booleans ---------------
279+-- | Make a JavaScript boolean value
224280valMakeBool :: JSBool -> JSC JSValueRef
225281valMakeBool b = do
226282 gctxt <- ask
227283 liftIO $ jsvaluemakeboolean gctxt b
228284
285+-- | Make a JavaScript boolean value
229286instance MakeValueRef Bool where
230287 makeValueRef = valMakeBool
231288
289+-- | Makes an argument list with just a single JavaScript boolean value
232290instance MakeArgRefs Bool where
233291 makeArgRefs b = valMakeBool b >>= (\ref -> return [ref])
234292
293+----------- numbers ---------------
294+-- | Make a JavaScript number
235295valMakeNumber :: JSNumber -> JSC JSValueRef
236296valMakeNumber n = do
237297 gctxt <- ask
238298 liftIO $ jsvaluemakenumber gctxt n
239299
300+-- | Makes a JavaScript number
240301instance MakeValueRef Double where
241302 makeValueRef = valMakeNumber
242303
304+-- | Makes an argument list with just a single JavaScript number
243305instance MakeArgRefs Double where
244306 makeArgRefs n = valMakeNumber n >>= (\ref -> return [ref])
245307
308+----------- numbers ---------------
309+-- | Make a JavaScript string
246310valMakeString :: Text -> JSC JSValueRef
247311valMakeString text = do
248312 gctxt <- ask
249313 s <- textToStr text
250314 liftIO $ jsvaluemakestring gctxt s
251315
316+-- | Makes a JavaScript string
252317instance MakeValueRef Text where
253318 makeValueRef = valMakeString
254319
320+-- | Makes an argument list with just a single JavaScript string
255321instance MakeArgRefs Text where
256322 makeArgRefs t = valMakeString t >>= (\ref -> return [ref])
257323
324+-- | Makes a JavaScript string
258325instance MakeValueRef String where
259326 makeValueRef = valMakeString . T.pack
260327
328+-- | Derefernce a value reference.
329+--
330+-- >>> runjs $ deRefVal JSNull
331+-- ValNull
332+-- >>> runjs $ deRefVal ()
333+-- ValUndefined
334+-- >>> runjs $ deRefVal True
335+-- ValBool True
336+-- >>> runjs $ deRefVal False
337+-- ValBool False
338+-- >>> runjs $ deRefVal (1.0 :: Double)
339+-- ValNumber 1.0
340+-- >>> runjs $ deRefVal (0.0 :: Double)
341+-- ValNumber 0.0
342+-- >>> runjs $ deRefVal ""
343+-- ValString ""
344+-- >>> runjs $ deRefVal "1"
345+-- ValString "1"
346+-- >>> runjs $ valToObject True >>= deRefVal
347+-- ValObject 0x...
261348deRefVal :: MakeValueRef val => val -> JSC JSValue
262349deRefVal val = do
263350 gctxt <- ask
@@ -271,8 +358,20 @@ deRefVal val = do
271358 Kjstypestring -> ValString <$> (valToStr valref >>= strToText)
272359 Kjstypeobject -> ValObject <$> valToObject valref
273360
361+-- | Make a JavaScript value out of a 'JSValue' ADT.
362+--
363+-- >>> runjs $ valMakeRef ValNull
364+-- "null"
365+-- >>> runjs $ valMakeRef ValUndefined
366+-- "undefined"
367+-- >>> runjs $ valMakeRef (ValBool True)
368+-- "true"
369+-- >>> runjs $ valMakeRef (ValNumber 1)
370+-- "1"
371+-- >>> runjs $ valMakeRef (ValString $ pack "Hello")
372+-- "Hello"
274373valMakeRef :: JSValue -> JSC JSValueRef
275−valMakeRef val = do
374+valMakeRef val =
276375 case val of
277376 ValNull -> valMakeNull
278377 ValUndefined -> valMakeUndefined
@@ -281,9 +380,14 @@ valMakeRef val = do
281380 ValString s -> valMakeString s
282381 ValObject o -> return o
283382
383+-- | Makes a JavaScript value from a 'JSValue' ADT.
284384instance MakeValueRef JSValue where
285385 makeValueRef = valMakeRef
286386
387+-- | Makes an argument list with just a single JavaScript value from a 'JSValue' ADT.
287388instance MakeArgRefs JSValue where
288389 makeArgRefs v = valMakeRef v >>= (\ref -> return [ref])
289390
391+instance MakeObjectRef JSNull where
392+ makeObjectRef = const valMakeNull
393+
tests/TestJSC.hsnew file+86
@@ -0,0 +1,86 @@
1+{-# LANGUAGE RecordWildCards #-}
2+-----------------------------------------------------------------------------
3+--
4+-- Module : TestJSC
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 Main (
15+ main
16+) where
17+
18+import Prelude hiding((!!))
19+import Graphics.UI.Gtk
20+ (Window, widgetDestroy, postGUIAsync, postGUISync, widgetShowAll,
21+ mainGUI, mainQuit, onDestroy, containerAdd, scrolledWindowNew,
22+ windowSetPosition, windowSetDefaultSize, timeoutAddFull, windowNew,
23+ initGUI)
24+import Control.Concurrent
25+ (tryTakeMVar, forkIO, newMVar, putMVar, takeMVar, newEmptyMVar,
26+ yield)
27+import System.Glib.MainLoop (priorityHigh)
28+import Graphics.UI.Gtk.General.Enums (WindowPosition(..))
29+import Graphics.UI.Gtk.WebKit.WebView
30+ (webViewGetMainFrame, webViewNew)
31+import System.IO.Unsafe (unsafePerformIO)
32+import Control.Monad.Trans.Reader (runReaderT)
33+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase (JSContextRef)
34+import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
35+ (webFrameGetGlobalContext)
36+import Language.Javascript.JSC
37+import Data.Text (Text)
38+import qualified Data.Text as T
39+import Control.Applicative
40+import Control.Monad.IO.Class (MonadIO(..))
41+
42+data TestState = TestState { jsContext :: JSContextRef, window :: Window }
43+
44+state = unsafePerformIO $ newMVar Nothing
45+done = unsafePerformIO $ newEmptyMVar
46+
47+runjs f = do
48+ tryTakeMVar done
49+ mbState <- takeMVar state
50+ TestState {..} <- case mbState of
51+ Nothing -> do
52+ newState <- newEmptyMVar
53+ forkIO $ do
54+ initGUI
55+ window <- windowNew
56+ timeoutAddFull (yield >> return True) priorityHigh 10
57+-- windowSetDefaultSize window 900 600
58+-- windowSetPosition window WinPosCenter
59+ scrollWin <- scrolledWindowNew Nothing Nothing
60+ webView <- webViewNew
61+ window `containerAdd` scrollWin
62+ scrollWin `containerAdd` webView
63+ window `onDestroy` do
64+ tryTakeMVar state
65+ putMVar state Nothing
66+ mainQuit
67+ putMVar done ()
68+ jsContext <- webViewGetMainFrame webView >>= webFrameGetGlobalContext
69+ putMVar newState TestState {..}
70+-- widgetShowAll window
71+ mainGUI
72+ takeMVar newState
73+ Just s -> return s
74+ x <- postGUISync $ runReaderT ((f >>= valToText) `catch` \ (JSException e) -> valToText e) jsContext
75+ putMVar state $ Just TestState {..}
76+ return x
77+
78+main = do
79+ runjs $ return ()
80+ Just TestState{..} <- takeMVar state
81+ postGUIAsync $ widgetDestroy window
82+ takeMVar done
83+
84+test = runjs $ global ! "console" ! "log" # ["Hello"]
85+
86+test2 = runjs $ eval "'Test'" >>= valToText