{-# LANGUAGE DefaultSignatures, TypeOperators, ScopedTypeVariables, DefaultSignatures, FlexibleContexts, FlexibleInstances, OverloadedStrings, TupleSections, MagicHash, CPP, JavaScriptFFI, ForeignFunctionInterface, UnliftedFFITypes, BangPatterns #-} module GHCJS.Marshal ( FromJSVal(..) , ToJSVal(..) , toJSVal_aeson , toJSVal_pure ) where import Control.Applicative import Control.Monad import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) import qualified Data.Aeson as AE import Data.Attoparsec.Number (Number(..)) import Data.Bits ((.&.)) import Data.Char (chr, ord) import qualified Data.HashMap.Strict as H import Data.Int (Int8, Int16, Int32) import qualified Data.JSString as JSS import qualified Data.JSString.Text as JSS import Data.Maybe import Data.Scientific (Scientific, scientific, fromFloatDigits) import Data.Text (Text) import qualified Data.Vector as V import Data.Word (Word8, Word16, Word32, Word) import Data.Primitive.ByteArray import Unsafe.Coerce (unsafeCoerce) import GHC.Int import GHC.Word import GHC.Types import GHC.Float import GHC.Prim import GHC.Generics import GHCJS.Types import GHCJS.Foreign.Internal import GHCJS.Marshal.Pure import qualified JavaScript.Array as A import qualified JavaScript.Array.Internal as AI import qualified JavaScript.Object as O import qualified JavaScript.Object.Internal as OI import GHCJS.Marshal.Internal instance FromJSVal JSVal where fromJSValUnchecked x = return x {-# INLINE fromJSValUnchecked #-} fromJSVal = return . Just {-# INLINE fromJSVal #-} instance FromJSVal () where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure -- {-# INLINE fromJSVal #-} instance FromJSVal a => FromJSVal [a] where fromJSVal = fromJSValListOf {-# INLINE fromJSVal #-} instance FromJSVal a => FromJSVal (Maybe a) where fromJSValUnchecked x | isUndefined x || isNull x = return Nothing | otherwise = fromJSVal x {-# INLINE fromJSValUnchecked #-} fromJSVal x | isUndefined x || isNull x = return (Just Nothing) | otherwise = fmap (fmap Just) fromJSVal x {-# INLINE fromJSVal #-} instance FromJSVal JSString where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Text where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Char where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} fromJSValUncheckedListOf = fromJSValUnchecked_pure {-# INLINE fromJSValListOf #-} fromJSValListOf = fromJSVal_pure {-# INLINE fromJSValUncheckedListOf #-} instance FromJSVal Bool where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Int where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Int8 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Int16 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Int32 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Word where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Word8 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Word16 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Word32 where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Float where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal Double where fromJSValUnchecked = fromJSValUnchecked_pure {-# INLINE fromJSValUnchecked #-} fromJSVal = fromJSVal_pure {-# INLINE fromJSVal #-} instance FromJSVal AE.Value where fromJSVal r = case jsonTypeOf r of JSONNull -> return (Just AE.Null) JSONInteger -> liftM (AE.Number . flip scientific 0 . (toInteger :: Int -> Integer)) <$> fromJSVal r JSONFloat -> liftM (AE.Number . (fromFloatDigits :: Double -> Scientific)) <$> fromJSVal r JSONBool -> liftM AE.Bool <$> fromJSVal r JSONString -> liftM AE.String <$> fromJSVal r JSONArray -> liftM (AE.Array . V.fromList) <$> fromJSVal r JSONObject -> do props <- OI.listProps (OI.Object r) runMaybeT $ do propVals <- forM props $ \p -> do v <- MaybeT (fromJSVal =<< OI.getProp p (OI.Object r)) return (JSS.textFromJSString p, v) return (AE.Object (H.fromList propVals)) {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b) => FromJSVal (a,b) where fromJSVal r = runMaybeT $ (,) <$> jf r 0 <*> jf r 1 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c) => FromJSVal (a,b,c) where fromJSVal r = runMaybeT $ (,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c, FromJSVal d) => FromJSVal (a,b,c,d) where fromJSVal r = runMaybeT $ (,,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 <*> jf r 3 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c, FromJSVal d, FromJSVal e) => FromJSVal (a,b,c,d,e) where fromJSVal r = runMaybeT $ (,,,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 <*> jf r 3 <*> jf r 4 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c, FromJSVal d, FromJSVal e, FromJSVal f) => FromJSVal (a,b,c,d,e,f) where fromJSVal r = runMaybeT $ (,,,,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 <*> jf r 3 <*> jf r 4 <*> jf r 5 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c, FromJSVal d, FromJSVal e, FromJSVal f, FromJSVal g) => FromJSVal (a,b,c,d,e,f,g) where fromJSVal r = runMaybeT $ (,,,,,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 <*> jf r 3 <*> jf r 4 <*> jf r 5 <*> jf r 6 {-# INLINE fromJSVal #-} instance (FromJSVal a, FromJSVal b, FromJSVal c, FromJSVal d, FromJSVal e, FromJSVal f, FromJSVal g, FromJSVal h) => FromJSVal (a,b,c,d,e,f,g,h) where fromJSVal r = runMaybeT $ (,,,,,,,) <$> jf r 0 <*> jf r 1 <*> jf r 2 <*> jf r 3 <*> jf r 4 <*> jf r 5 <*> jf r 6 <*> jf r 7 {-# INLINE fromJSVal #-} jf :: FromJSVal a => JSVal -> Int -> MaybeT IO a jf r n = MaybeT $ do r' <- AI.read n (AI.SomeJSArray r) if isUndefined r then return Nothing else fromJSVal r' instance ToJSVal JSVal where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal AE.Value where toJSVal = toJSVal_aeson {-# INLINE toJSVal #-} instance ToJSVal JSString where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Text where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Char where toJSVal = return . pToJSVal {-# INLINE toJSVal #-} toJSValListOf = return . pToJSVal {-# INLINE toJSValListOf #-} instance ToJSVal Bool where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Int where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Int8 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Int16 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Int32 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Word where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Word8 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Word16 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Word32 where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Float where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal Double where toJSVal = toJSVal_pure {-# INLINE toJSVal #-} instance ToJSVal a => ToJSVal [a] where toJSVal = toJSValListOf {-# INLINE toJSVal #-} instance ToJSVal a => ToJSVal (Maybe a) where toJSVal Nothing = return jsNull toJSVal (Just a) = toJSVal a {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b) => ToJSVal (a,b) where toJSVal (a,b) = join $ arr2 <$> toJSVal a <*> toJSVal b {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b, ToJSVal c) => ToJSVal (a,b,c) where toJSVal (a,b,c) = join $ arr3 <$> toJSVal a <*> toJSVal b <*> toJSVal c {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b, ToJSVal c, ToJSVal d) => ToJSVal (a,b,c,d) where toJSVal (a,b,c,d) = join $ arr4 <$> toJSVal a <*> toJSVal b <*> toJSVal c <*> toJSVal d {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b, ToJSVal c, ToJSVal d, ToJSVal e) => ToJSVal (a,b,c,d,e) where toJSVal (a,b,c,d,e) = join $ arr5 <$> toJSVal a <*> toJSVal b <*> toJSVal c <*> toJSVal d <*> toJSVal e {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b, ToJSVal c, ToJSVal d, ToJSVal e, ToJSVal f) => ToJSVal (a,b,c,d,e,f) where toJSVal (a,b,c,d,e,f) = join $ arr6 <$> toJSVal a <*> toJSVal b <*> toJSVal c <*> toJSVal d <*> toJSVal e <*> toJSVal f {-# INLINE toJSVal #-} instance (ToJSVal a, ToJSVal b, ToJSVal c, ToJSVal d, ToJSVal e, ToJSVal f, ToJSVal g) => ToJSVal (a,b,c,d,e,f,g) where toJSVal (a,b,c,d,e,f,g) = join $ arr7 <$> toJSVal a <*> toJSVal b <*> toJSVal c <*> toJSVal d <*> toJSVal e <*> toJSVal f <*> toJSVal g {-# INLINE toJSVal #-} foreign import javascript unsafe "[$1]" arr1 :: JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2]" arr2 :: JSVal -> JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2,$3]" arr3 :: JSVal -> JSVal -> JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2,$3,$4]" arr4 :: JSVal -> JSVal -> JSVal -> JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2,$3,$4,$5]" arr5 :: JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2,$3,$4,$5,$6]" arr6 :: JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> IO JSVal foreign import javascript unsafe "[$1,$2,$3,$4,$5,$6,$7]" arr7 :: JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> JSVal -> IO JSVal toJSVal_aeson :: AE.ToJSON a => a -> IO JSVal toJSVal_aeson x = cv (AE.toJSON x) where cv = convertValue convertValue :: AE.Value -> IO JSVal convertValue AE.Null = return jsNull convertValue (AE.String t) = return (pToJSVal t) convertValue (AE.Array a) = (\(AI.SomeJSArray x) -> x) <$> (AI.fromListIO =<< mapM convertValue (V.toList a)) convertValue (AE.Number n) = toJSVal (realToFrac n :: Double) convertValue (AE.Bool b) = return (toJSBool b) convertValue (AE.Object o) = do obj@(OI.Object obj') <- OI.create mapM_ (\(k,v) -> convertValue v >>= \v' -> OI.setProp (JSS.textToJSString k) v' obj) (H.toList o) return obj'