ghcjs / ghcjs-base

base library for GHCJS for JavaScript interaction and marshalling, used by higher level libraries like JSC
MIT License
45 stars 67 forks source link

How does a `FromJSVal` instance affect FFI? #69

Closed crocket closed 7 years ago

crocket commented 7 years ago

According to my experiments, any Type with a PFromJSVal instance can be used as Nullable Type in FFI signatures.

I conjecture that FromJSVal instances affect FFI in a similar way.

Does anyone know about this?

Below is a full demonstration of how PFromJSVal affects FFI.

{-# LANGUAGE JavaScriptFFI, OverloadedStrings #-}
module Main where

import qualified Data.JSString as S
import qualified GHCJS.Types as T
import qualified GHCJS.Marshal as M
import qualified GHCJS.Marshal.Pure as MP
import qualified JavaScript.Object as O
import System.IO
import Unsafe.Coerce
import Control.Monad (forever)
import Data.Maybe (fromJust)
import qualified Data.Aeson as AE
import GHCJS.Nullable
import qualified Data.Time.Format as TF
import qualified Data.Time.Clock as TC

foreign import javascript interruptible
  "require('fs').stat($1, function (err, stat) { $c(err,stat) });"
  js_fsStat :: S.JSString -> IO (T.JSVal, Nullable FsStat)
foreign import javascript unsafe
  "$2[$1]" js_getProp :: S.JSString -> O.Object -> T.JSVal

newtype Time = Time TC.UTCTime deriving Show

foreign import javascript unsafe
  "$r=$1.toUTCString();" js_Date_toUTCString :: T.JSVal -> S.JSString

instance MP.PFromJSVal Time where
  pFromJSVal jsval = Time $ timeFunc utcDateString
    where utcDateString = S.unpack $ js_Date_toUTCString jsval
          timeFunc =
            TF.parseTimeOrError False TF.defaultTimeLocale TF.rfc822DateFormat

data FsStat = FsStat
  { dev :: Int , ino :: Int , mode :: Int
  , nlink :: Int , uid :: Int , gid :: Int
  , rdev :: Int , size :: Int , blksize :: Int
  , blocks :: Int , atime :: Time, mtime :: Time
  , ctime :: Time , birthtime :: Time } deriving Show

instance MP.PFromJSVal FsStat where
  pFromJSVal jsval =
    let obj = unsafeCoerce jsval
        dev' = MP.pFromJSVal $ js_getProp "dev" obj
        ino' = MP.pFromJSVal $ js_getProp "ino" obj
        mode' = MP.pFromJSVal $ js_getProp "mode" obj
        nlink' = MP.pFromJSVal $ js_getProp "nlink" obj
        uid' = MP.pFromJSVal $ js_getProp "uid" obj
        gid' = MP.pFromJSVal $ js_getProp "gid" obj
        rdev' = MP.pFromJSVal $ js_getProp "rdev" obj
        size' = MP.pFromJSVal $ js_getProp "size" obj
        blksize' = MP.pFromJSVal $ js_getProp "blksize" obj
        blocks' = MP.pFromJSVal $ js_getProp "blocks" obj
        atime' = MP.pFromJSVal $ js_getProp "atime" obj
        mtime' = MP.pFromJSVal $ js_getProp "mtime" obj
        ctime' = MP.pFromJSVal $ js_getProp "ctime" obj
        birthtime' = MP.pFromJSVal $ js_getProp "birthtime" obj
    in FsStat { dev = dev', ino = ino', mode=mode', nlink=nlink',
                uid=uid', gid=gid', rdev=rdev', size=size',
                blksize=blksize', blocks=blocks', atime=atime',
                mtime=mtime', ctime=ctime', birthtime=birthtime'}

fsStat :: String -> IO (AE.Value, Maybe FsStat)
fsStat f = do
  (err, stats) <- js_fsStat $ S.pack f
  err' <- fromJust <$> M.fromJSVal err
  return (err', nullableToMaybe stats)

prompt :: String -> IO String
prompt text = putStr text >> hFlush stdout >> getLine

main :: IO ()
main = forever $ do
  file <- prompt "Enter file name: "
  (err, fsstat) <- fsStat file
  case err of
    AE.Null -> putStrLn "No Error"
    AE.Object obj -> putStrLn $ show obj
    _ -> putStrLn $ "Unexpected data : " ++ (show err)
  putStrLn "-------------------------------------"
  case fsstat of
    Nothing -> putStrLn "No file stat"
    Just s -> putStrLn $ show s
crocket commented 7 years ago

After numerous experiments, I figured out that newtypes wrapping JSVal can subsitute JSVal in FFI signatures and that Nullable is just another wrapper around JSVal.

I surmise the key to understanding GHCJS FFI lies in understanding GHC FFI.

crocket commented 7 years ago

I concluded that FromJSVal and its siblings don't affect FFI.