packages feed

libffi-dynamic-0.0.0.1: src/Foreign/LibFFI/Dynamic/CIF.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
module Foreign.LibFFI.Dynamic.CIF
    ( ABI(..)
    , defaultABI
    
    , SomeCIF(..)
    , getCIF
    
    , CIF(..)
    , toSomeCIF
    , cif
    , cifWithABI
    
    , abi
    , retType
    , argTypes, nArgs
    , cifFlags
    
    , SigType, SigReturn
    , retTypeOf, argTypesOf
    , call, callWithABI, callWithCIF
    ) where

import Control.Applicative
import Data.Hashable
import Data.Interned
import Data.List
import Foreign.LibFFI.Dynamic.Base
import Foreign.LibFFI.Dynamic.FFIType
import Foreign.LibFFI.Dynamic.Type
import Foreign.C.Types
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import System.IO.Unsafe

#include <ffi.h>

newtype ABI = ABI CInt
    deriving (Eq, Ord, Show, Storable)

instance Hashable ABI where
    hashWithSalt salt (ABI x) = 
        hashWithSalt salt (fromIntegral x :: Int)

defaultABI = ABI (#const FFI_DEFAULT_ABI)

newtype SomeCIF = SomeCIF (Ptr SomeCIF)
    deriving (Eq, Ord, Show)

instance Interned SomeCIF where
    data Description SomeCIF = Sig ABI SomeType [SomeType]
        deriving (Eq, Show)
    type Uninterned SomeCIF = Description SomeCIF
    
    describe = id
    identify _ (Sig abi ret args) = unsafePerformIO $ do
        -- these should not be freed as long as the returned @a@ is reachable
        cif <- SomeCIF <$> mallocBytes (#size ffi_cif)
        
        let nArgs = fromIntegral (length args)
        argTypes <- newArray args
        
        -- TODO: check return code
        ffi_prep_cif cif abi nArgs ret argTypes 
        
        return cif
    
    cache = cifCache

{-# NOINLINE cifCache #-}
cifCache :: Cache SomeCIF
cifCache = mkCache

instance Hashable (Description SomeCIF) where
    hashWithSalt salt (Sig abi ret args) =
        foldl' hashWithSalt (hashWithSalt salt abi) (ret : args)

foreign import ccall ffi_prep_cif :: SomeCIF -> ABI -> CInt -> SomeType -> Ptr SomeType -> IO FFI_Status

getCIF :: ABI -> SomeType -> [SomeType] -> SomeCIF
getCIF abi retType argTypes = intern (Sig abi retType argTypes)

class SigType t where
    type SigReturn t
    
    retTypeOf'  :: p t ->  SomeType
    argTypesOf' :: p t -> [SomeType]

retTypeOf :: SigType t => p t -> SomeType
retTypeOf = retTypeOf'

argTypesOf :: SigType t => p t -> [SomeType]
argTypesOf = argTypesOf'

instance FFIType t => SigType (IO t) where
    type SigReturn (IO t) = t
    
    retTypeOf' = ffiTypeOf_ . (const Nothing :: p (IO b) -> Maybe b)
    argTypesOf' _ = []

instance (FFIType a, SigType b) => SigType (a -> b) where
    type SigReturn (a -> b) = SigReturn b
    
    retTypeOf' = retTypeOf . (const Nothing :: p (a -> b) -> Maybe b)
    argTypesOf' p
        = ffiTypeOf_ ((const Nothing :: p (a -> b) -> Maybe a) p)
        : argTypesOf ((const Nothing :: p (a -> b) -> Maybe b) p)

newtype CIF a = CIF SomeCIF
    deriving (Eq, Ord, Show)

toSomeCIF :: CIF a -> SomeCIF
toSomeCIF (CIF c) = c

cif :: SigType t => CIF t
cif = cifWithABI defaultABI

cifWithABI :: SigType t => ABI -> CIF t
cifWithABI abi = theCIF
    where
        theCIF = CIF (getCIF abi (retTypeOf theCIF) (argTypesOf theCIF))

call :: SigType t => FunPtr t -> Ptr (SigReturn t) -> Ptr (Ptr ()) -> IO ()
call = callWithCIF theCIF
    where
        {-# NOINLINE theCIF #-}
        theCIF = cif

callWithABI :: SigType t => ABI -> FunPtr t -> Ptr (SigReturn t) -> Ptr (Ptr ()) -> IO ()
callWithABI abi = callWithCIF theCIF
    where 
        {-# NOINLINE theCIF #-}
        theCIF = cifWithABI abi

foreign import ccall "ffi_call"
    callWithCIF :: CIF a -> FunPtr a -> Ptr (SigReturn a) -> Ptr (Ptr ()) -> IO ()

abi :: SomeCIF -> ABI
abi (SomeCIF p) = unsafePerformIO $ do
    (#peek ffi_cif, abi) p

retType :: SomeCIF -> SomeType
retType (SomeCIF p) = unsafePerformIO $ do
    (#peek ffi_cif, rtype) p

argTypes :: SomeCIF -> [SomeType]
argTypes cif@(SomeCIF p) = unsafePerformIO $ do
    ts <- (#peek ffi_cif, arg_types) p :: IO (Ptr SomeType)
    peekArray (nArgs cif) ts

nArgs :: SomeCIF -> Int
nArgs (SomeCIF p) = unsafePerformIO $ do
    n  <- (#peek ffi_cif, nargs) p :: IO CUInt
    return $! fromIntegral n

cifFlags :: SomeCIF -> CUInt
cifFlags (SomeCIF p) = unsafePerformIO $ do
    (#peek ffi_cif, flags) p