haskell-gi-0.10: src/GI/Callable.hs
{-# LANGUAGE LambdaCase #-}
module GI.Callable
( genCallable
, hOutType
, arrayLengths
, arrayLengthsMap
, wrapMaybe
, inArgInterfaces
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
#endif
import Control.Monad (forM, forM_, when)
import Data.Bool (bool)
import Data.List (intercalate, nub, (\\))
import Data.Maybe (isJust)
import Data.Typeable (TypeRep, typeOf)
import qualified Data.Map as Map
import qualified Data.Text as T
import Data.Text (Text)
import GI.API
import GI.Code
import GI.Conversions
import GI.SymbolNaming
import GI.Transfer
import GI.Type
import GI.Util
import Text.Show.Pretty (ppShow)
hOutType :: Callable -> [Arg] -> Bool -> ExcCodeGen TypeRep
hOutType callable outArgs ignoreReturn = do
hReturnType <- case returnType callable of
TBasicType TVoid -> return $ typeOf ()
_ -> if ignoreReturn
then return $ typeOf ()
else haskellType $ returnType callable
hOutArgTypes <- forM outArgs $ \outarg ->
wrapMaybe outarg >>= bool
(haskellType (argType outarg))
(maybeT <$> haskellType (argType outarg))
let maybeHReturnType = if returnMayBeNull callable && not ignoreReturn
then maybeT hReturnType
else hReturnType
return $ case (outArgs, show maybeHReturnType) of
([], _) -> maybeHReturnType
(_, "()") -> "(,)" `con` hOutArgTypes
_ -> "(,)" `con` (maybeHReturnType : hOutArgTypes)
mkForeignImport :: Text -> Callable -> Bool -> CodeGen ()
mkForeignImport symbol callable throwsGError = foreignImport $ do
line first
indent $ do
mapM_ (\a -> line =<< fArgStr a) (args callable)
when throwsGError $
line $ padTo 40 "Ptr (Ptr GError) -> " ++ "-- error"
line =<< last
where
first = "foreign import ccall \"" ++ T.unpack symbol ++ "\" " ++
T.unpack symbol ++ " :: "
fArgStr arg = do
ft <- foreignType $ argType arg
weAlloc <- isJust <$> requiresAlloc (argType arg)
let ft' = if direction arg == DirectionIn || weAlloc then
ft
else
ptr ft
let start = show ft' ++ " -> "
return $ padTo 40 start ++ "-- " ++ T.unpack (argName arg)
++ " : " ++ show (argType arg)
last = show <$> io <$> case returnType callable of
TBasicType TVoid -> return $ typeOf ()
_ -> foreignType (returnType callable)
-- Given an argument to a function, return whether it should be
-- wrapped in a maybe type (useful for nullable types). We do some
-- sanity checking to make sure that the argument is actually nullable
-- (a relatively common annotation mistake is to mix up (optional)
-- with (nullable)).
wrapMaybe :: Arg -> ExcCodeGen Bool
wrapMaybe arg =
if mayBeNull arg
then case argType arg of
-- NULL GLists and GSLists are semantically the same as an
-- empty list, so they don't need a Maybe wrapper on their
-- type.
TGList _ -> return False
TGSList _ -> return False
_ -> do
nullable <- isNullable (argType arg)
if nullable
then return True
else badIntroError $ "argument \"" ++ T.unpack (argName arg)
++ "\" is not of nullable type (" ++ show (argType arg)
++ "), but it is marked as such."
else return False
-- Given the list of arguments returns the list of constraints and the
-- list of types in the signature.
inArgInterfaces :: [Arg] -> ExcCodeGen ([String], [String])
inArgInterfaces inArgs = consAndTypes (['a'..'z'] \\ ['m']) inArgs
where
consAndTypes :: [Char] -> [Arg] -> ExcCodeGen ([String], [String])
consAndTypes _ [] = return ([], [])
consAndTypes letters (arg:args) = do
(ls, t, cons) <- argumentType letters $ argType arg
t' <- wrapMaybe arg >>= bool (return t)
(return $ "Maybe (" ++ t ++ ")")
(restCons, restTypes) <- consAndTypes ls args
return (cons ++ restCons, t' : restTypes)
-- Given a callable, return a list of (array, length) pairs, where in
-- each pair "length" is the argument holding the length of the
-- (non-zero-terminated, non-fixed size) C array.
arrayLengthsMap :: Callable -> [(Arg, Arg)] -- List of (array, length)
arrayLengthsMap callable = go (args callable) []
where
go :: [Arg] -> [(Arg, Arg)] -> [(Arg, Arg)]
go [] acc = acc
go (a:as) acc = case argType a of
TCArray False fixedSize length _ ->
if fixedSize > -1 || length == -1
then go as acc
else go as $ (a, (args callable)!!length) : acc
_ -> go as acc
-- Return the list of arguments of the callable that contain length
-- arguments, including a possible length for the result of calling
-- the function.
arrayLengths :: Callable -> [Arg]
arrayLengths callable = map snd (arrayLengthsMap callable) ++
-- Often one of the arguments is just the length of
-- the result.
case returnType callable of
TCArray False (-1) length _ ->
if length > -1
then [(args callable)!!length]
else []
_ -> []
-- This goes through a list of [(a,b)], and tags every entry where the
-- "b" field has occurred before with the value of "a" for which it
-- occurred. (The first appearance is not tagged.)
classifyDuplicates :: Ord b => [(a, b)] -> [(a, b, Maybe a)]
classifyDuplicates args = doClassify Map.empty args
where doClassify :: Ord b => Map.Map b a -> [(a, b)] -> [(a, b, Maybe a)]
doClassify _ [] = []
doClassify found ((value, key):args) =
(value, key, Map.lookup key found) :
doClassify (Map.insert key value found) args
-- Read the length of in array arguments from the corresponding
-- Haskell objects. A subtlety is that sometimes a single length
-- argument is expected from the C side to encode the length of
-- various lists. Ideally we would encode this in the types, but the
-- resulting API would be rather cumbersome. We insted perform runtime
-- checks to make sure that the given lists have the same length.
readInArrayLengths :: Name -> Callable -> [Arg] -> ExcCodeGen ()
readInArrayLengths name callable hInArgs = do
let lengthMaps = classifyDuplicates $ arrayLengthsMap callable
forM_ lengthMaps $ \(array, length, duplicate) ->
when (array `elem` hInArgs) $
case duplicate of
Nothing -> readInArrayLength array length
Just previous -> checkInArrayLength name array length previous
-- Read the length of an array into the corresponding variable.
readInArrayLength :: Arg -> Arg -> ExcCodeGen ()
readInArrayLength array length = do
let lvar = escapeReserved $ argName length
avar = escapeReserved $ argName array
wrapMaybe array >>= bool
(do
al <- computeArrayLength avar (argType array)
line $ "let " ++ lvar ++ " = " ++ al)
(do
line $ "let " ++ lvar ++ " = case " ++ avar ++ " of"
indent $ indent $ do
line $ "Nothing -> 0"
let jarray = "j" ++ ucFirst avar
al <- computeArrayLength jarray (argType array)
line $ "Just " ++ jarray ++ " -> " ++ al)
-- Check that the given array has a length equal to the given length
-- variable.
checkInArrayLength :: Name -> Arg -> Arg -> Arg -> ExcCodeGen ()
checkInArrayLength n array length previous = do
name <- lowerName n
let funcName = namespace n ++ "." ++ name
lvar = escapeReserved $ argName length
avar = escapeReserved $ argName array
expectedLength = avar ++ "_expected_length_"
pvar = escapeReserved $ argName previous
wrapMaybe array >>= bool
(do
al <- computeArrayLength avar (argType array)
line $ "let " ++ expectedLength ++ " = " ++ al)
(do
line $ "let " ++ expectedLength ++ " = case " ++ avar ++ " of"
indent $ indent $ do
line $ "Nothing -> 0"
let jarray = "j" ++ ucFirst avar
al <- computeArrayLength jarray (argType array)
line $ "Just " ++ jarray ++ " -> " ++ al)
line $ "when (" ++ expectedLength ++ " /= " ++ lvar ++ ") $"
indent $ line $ "error \"" ++ funcName ++ " : length of '" ++ avar ++
"' does not agree with that of '" ++ pvar ++ "'.\""
-- Whether to skip the return value in the generated bindings. The
-- C convention is that functions throwing an error and returning
-- a gboolean set the boolean to TRUE iff there is no error, so
-- the information is always implicit in whether we emit an
-- exception or not, so the return value can be omitted from the
-- generated bindings without loss of information (and omitting it
-- gives rise to a nicer API). See
-- https://bugzilla.gnome.org/show_bug.cgi?id=649657
skipRetVal :: Callable -> Bool -> Bool
skipRetVal callable throwsGError =
(skipReturn callable) ||
(throwsGError && returnType callable == TBasicType TBoolean)
freeInArgs' :: (Arg -> String -> String -> ExcCodeGen [String]) ->
Callable -> Map.Map String String -> ExcCodeGen [String]
freeInArgs' freeFn callable nameMap = concat <$> actions
where
actions :: ExcCodeGen [[String]]
actions = forM (args callable) $ \arg ->
case Map.lookup (escapeReserved $ argName arg) nameMap of
Just name -> freeFn arg name $
-- Pass in the length argument in case it's needed.
case argType arg of
TCArray False (-1) (-1) _ -> undefined
TCArray False (-1) length _ ->
escapeReserved $ argName $ (args callable)!!length
_ -> undefined
Nothing -> badIntroError $ "freeInArgs: do not understand " ++ show arg
-- Return the list of actions freeing the memory associated with the
-- callable variables. This is run if the call to the C function
-- succeeds, if there is an error freeInArgsOnError below is called
-- instead.
freeInArgs = freeInArgs' freeInArg
-- Return the list of actions freeing the memory associated with the
-- callable variables. This is run in case there is an error during
-- the call.
freeInArgsOnError = freeInArgs' freeInArgOnError
-- Marshall the haskell arguments into their corresponding C
-- equivalents. omitted gives a list of DirectionIn arguments that
-- should be ignored, as they will be dealt with separately.
prepareArgForCall :: [Arg] -> Arg -> ExcCodeGen String
prepareArgForCall omitted arg = do
isCallback <- findAPI (argType arg) >>=
\case Just (APICallback _) -> return True
_ -> return False
when (isCallback && direction arg /= DirectionIn) $
notImplementedError "Only callbacks with DirectionIn are supported"
case direction arg of
DirectionIn -> if arg `elem` omitted
then return . escapeReserved . argName $ arg
else if isCallback
then prepareInCallback arg
else prepareInArg arg
DirectionInout -> prepareInoutArg arg
DirectionOut -> prepareOutArg arg
prepareInArg :: Arg -> ExcCodeGen String
prepareInArg arg = do
let name = escapeReserved $ argName arg
wrapMaybe arg >>= bool
(convert name $ hToF (argType arg) (transfer arg))
(do
let maybeName = "maybe" ++ ucFirst name
line $ maybeName ++ " <- case " ++ name ++ " of"
indent $ do
line $ "Nothing -> return nullPtr"
let jName = "j" ++ ucFirst name
line $ "Just " ++ jName ++ " -> do"
indent $ do
converted <- convert jName $ hToF (argType arg)
(transfer arg)
line $ "return " ++ converted
return maybeName)
-- Callbacks are a fairly special case, we treat them separately.
prepareInCallback :: Arg -> ExcCodeGen String
prepareInCallback arg = do
let name = escapeReserved $ argName arg
ptrName = "ptr" ++ name
scope = argScope arg
(maker, wrapper) <- case argType arg of
(TInterface ns n) ->
do
prefix <- qualify ns
return $ (prefix ++ "mk" ++ n,
prefix ++ lcFirst n ++ "Wrapper")
_ -> error $ "prepareInCallback : Not an interface! " ++ ppShow arg
when (scope == ScopeTypeAsync) $ do
ft <- show <$> foreignType (argType arg)
line $ ptrName ++ " <- callocMem :: IO (Ptr (" ++ ft ++ "))"
wrapMaybe arg >>= bool
(do
let name' = prime name
p = if (scope == ScopeTypeAsync)
then parenthesize $ "Just " ++ ptrName
else "Nothing"
line $ name' ++ " <- " ++ maker ++ " "
++ parenthesize (wrapper ++ " " ++ p ++ " " ++ name)
when (scope == ScopeTypeAsync) $
line $ "poke " ++ ptrName ++ " " ++ name'
return name')
(do
let maybeName = "maybe" ++ ucFirst name
line $ maybeName ++ " <- case " ++ name ++ " of"
indent $ do
line $ "Nothing -> return (castPtrToFunPtr nullPtr)"
let jName = "j" ++ ucFirst name
jName' = prime jName
line $ "Just " ++ jName ++ " -> do"
indent $ do
let p = if (scope == ScopeTypeAsync)
then parenthesize $ "Just " ++ ptrName
else "Nothing"
line $ jName' ++ " <- " ++ maker ++ " "
++ parenthesize (wrapper ++ " "
++ p ++ " " ++ jName)
when (scope == ScopeTypeAsync) $
line $ "poke " ++ ptrName ++ " " ++ jName'
line $ "return " ++ jName'
return maybeName)
prepareInoutArg :: Arg -> ExcCodeGen String
prepareInoutArg arg = do
name' <- prepareInArg arg
ft <- foreignType $ argType arg
allocInfo <- requiresAlloc (argType arg)
case allocInfo of
Just (isBoxed, n) -> do
let allocator = if isBoxed
then "callocBoxedBytes"
else "callocBytes"
wrapMaybe arg >>= bool
(do
name'' <- genConversion (prime name') $
literal $ M $ allocator ++ " " ++ show n ++
" :: " ++ show (io ft)
line $ "memcpy " ++ name'' ++ " " ++ name' ++ " " ++ show n
return name'')
-- The semantics of this case are somewhat undefined.
(notImplementedError "Nullable inout structs not supported")
Nothing -> do
name'' <- genConversion (prime name') $
literal $ M $ "allocMem :: " ++ show (io $ ptr ft)
line $ "poke " ++ name'' ++ " " ++ name'
return name''
prepareOutArg :: Arg -> CodeGen String
prepareOutArg arg = do
let name = escapeReserved $ argName arg
ft <- foreignType $ argType arg
allocInfo <- requiresAlloc (argType arg)
case allocInfo of
Just (isBoxed, n) -> do
let allocator = if isBoxed
then "callocBoxedBytes"
else "callocBytes"
genConversion name $ literal $ M $ allocator ++ " " ++ show n ++
" :: " ++ show (io ft)
Nothing ->
genConversion name $
literal $ M $ "allocMem :: " ++ show (io $ ptr ft)
-- Convert a non-zero terminated out array, stored in a variable
-- named "aname", into the corresponding Haskell object.
convertOutCArray :: Callable -> Type -> String -> Map.Map String String ->
Transfer -> ExcCodeGen String
convertOutCArray callable t@(TCArray False fixed length _) aname
nameMap transfer = do
if fixed > -1
then do
unpacked <- convert aname $ unpackCArray (show fixed) t transfer
-- Free the memory associated with the array
freeContainerType transfer t aname undefined
return unpacked
else do
when (length == -1) $
badIntroError $ "Unknown length for \"" ++ aname ++ "\""
let lname = escapeReserved $ argName $ (args callable)!!length
lname' <- case Map.lookup lname nameMap of
Just n -> return n
Nothing ->
badIntroError $ "Couldn't find out array length " ++
lname
let lname'' = prime lname'
unpacked <- convert aname $ unpackCArray lname'' t transfer
-- Free the memory associated with the array
freeContainerType transfer t aname lname''
return unpacked
-- Remove the warning, this should never be reached.
convertOutCArray _ t _ _ _ =
error $ "convertOutCArray : unexpected " ++ show t
-- Read the array lengths for out arguments.
readOutArrayLengths :: Callable -> Map.Map String String -> ExcCodeGen ()
readOutArrayLengths callable nameMap = do
let lNames = nub $ map (escapeReserved . argName) $
filter ((/= DirectionIn) . direction) $
arrayLengths callable
forM_ lNames $ \lname -> do
lname' <- case Map.lookup lname nameMap of
Just n -> return n
Nothing ->
badIntroError $ "Couldn't find out array length " ++
lname
genConversion lname' $ apply $ M "peek"
-- Touch DirectionIn arguments so we are sure that they exist when the
-- C function was called.
touchInArg :: Arg -> ExcCodeGen ()
touchInArg arg = when (direction arg /= DirectionOut) $ do
let name = escapeReserved $ argName arg
case elementType (argType arg) of
Just a -> do
managed <- isManaged a
when managed $ wrapMaybe arg >>= bool
(line $ "mapM_ touchManagedPtr " ++ name)
(line $ "whenJust " ++ name ++ " (mapM_ touchManagedPtr)")
Nothing -> do
managed <- isManaged (argType arg)
when managed $ wrapMaybe arg >>= bool
(line $ "touchManagedPtr " ++ name)
(line $ "whenJust " ++ name ++ " touchManagedPtr")
-- Find the association between closure arguments and their
-- corresponding callback.
closureToCallbackMap :: Callable -> ExcCodeGen (Map.Map Int Arg)
closureToCallbackMap callable =
-- The introspection info does not specify the closure for destroy
-- notify's associated with a callback, since it is implicitly the
-- same one as the ScopeTypeNotify callback associated with the
-- DestroyNotify.
go (filter (not . (`elem` destroyers)) $ args callable) Map.empty
where destroyers = map (args callable!!) . filter (/= -1) . map argDestroy
$ args callable
go :: [Arg] -> Map.Map Int Arg -> ExcCodeGen (Map.Map Int Arg)
go [] m = return m
go (arg:as) m =
if argScope arg == ScopeTypeInvalid
then go as m
else case argClosure arg of
(-1) -> go as m
c -> case Map.lookup c m of
Just _ -> notImplementedError $
"Closure for multiple callbacks unsupported"
++ ppShow arg ++ "\n"
++ ppShow callable
Nothing -> go as $ Map.insert c arg m
-- user_data style arguments.
prepareClosures :: Callable -> Map.Map String String -> ExcCodeGen ()
prepareClosures callable nameMap = do
m <- closureToCallbackMap callable
let closures = filter (/= -1) . map argClosure $ args callable
forM_ closures $ \closure ->
case Map.lookup closure m of
Nothing -> badIntroError $ "Closure not found! "
++ ppShow callable
++ "\n" ++ ppShow m
++ "\n" ++ show closure
Just cb -> do
let closureName = escapeReserved $ argName $ (args callable)!!closure
n = escapeReserved $ argName cb
n' <- case Map.lookup n nameMap of
Just n -> return n
Nothing -> badIntroError $ "Cannot find closure name!! "
++ ppShow callable ++ "\n"
++ ppShow nameMap
case argScope cb of
ScopeTypeInvalid -> badIntroError $ "Invalid scope! "
++ ppShow callable
ScopeTypeNotified -> do
line $ "let " ++ closureName ++ " = castFunPtrToPtr " ++ n'
case argDestroy cb of
(-1) -> badIntroError $
"ScopeTypeNotified without destructor! "
++ ppShow callable
k -> let destroyName =
escapeReserved . argName $ (args callable)!!k in
line $ "let " ++ destroyName ++ " = safeFreeFunPtrPtr"
ScopeTypeAsync ->
line $ "let " ++ closureName ++ " = nullPtr"
ScopeTypeCall -> line $ "let " ++ closureName ++ " = nullPtr"
freeCallCallbacks :: Callable -> Map.Map String String -> ExcCodeGen ()
freeCallCallbacks callable nameMap =
forM_ (args callable) $ \arg -> do
let name = escapeReserved $ argName arg
name' <- case Map.lookup name nameMap of
Just n -> return n
Nothing -> badIntroError $ "Could not find " ++ name
++ " in " ++ ppShow callable ++ "\n"
++ ppShow nameMap
when (argScope arg == ScopeTypeCall) $
line $ "safeFreeFunPtr $ castFunPtrToPtr " ++ name'
hSignature :: [Arg] -> TypeRep -> ExcCodeGen ()
hSignature hInArgs retType = do
(argConstraints, types) <- inArgInterfaces hInArgs
indent $ do
line $ "(" ++ intercalate ", " ("MonadIO m" : argConstraints) ++ ") =>"
forM_ (zip types hInArgs) $ \(t, a) ->
line $ withComment (t ++ " ->") $ T.unpack (argName a)
line . show $ "m" `con` [retType]
genCallable :: Name -> Text -> Callable -> Bool -> ExcCodeGen ()
genCallable n symbol callable throwsGError = do
group $ do
line $ "-- Args : " ++ (show $ args callable)
line $ "-- Lengths : " ++ (show $ arrayLengths callable)
line $ "-- hInArgs : " ++ show hInArgs
line $ "-- returnType : " ++ (show $ returnType callable)
line $ "-- throws : " ++ (show throwsGError)
line $ "-- Skip return : " ++ (show $ skipReturn callable)
when (skipReturn callable && returnType callable /= TBasicType TBoolean) $
do line "-- XXX return value ignored, but it is not a boolean."
line "-- This may be a memory leak?"
mkForeignImport symbol callable throwsGError
blank
wrapper
where
inArgs = filter ((/= DirectionOut) . direction) $ args callable
-- We do not expose user_data arguments, destroynotify arguments,
-- and C array length arguments to Haskell code.
closures = map (args callable!!) . filter (/= -1) . map argClosure $ inArgs
destroyers = map (args callable!!) . filter (/= -1) . map argDestroy $ inArgs
omitted = arrayLengths callable ++ closures ++ destroyers
hInArgs = filter (`notElem` omitted) inArgs
outArgs = filter ((/= DirectionIn) . direction) $ args callable
hOutArgs = filter (`notElem` (arrayLengths callable)) outArgs
ignoreReturn = skipRetVal callable throwsGError
wrapper = group $ do
let argName' = escapeReserved . argName
name <- lowerName n
line $ deprecatedPragma name $ callableDeprecated callable
line $ name ++ " ::"
hSignature hInArgs =<< hOutType callable hOutArgs ignoreReturn
line $ name ++ " " ++ intercalate " " (map argName' hInArgs) ++ " = liftIO $ do"
indent $ do
readInArrayLengths n callable hInArgs
inArgNames <- forM (args callable) $ \arg ->
prepareArgForCall omitted arg
-- Map from argument names to names passed to the C function
let nameMap = Map.fromList $ flip zip inArgNames
$ map argName' $ args callable
prepareClosures callable nameMap
if throwsGError
then do
line "onException (do"
indent $ do
invokeCFunction inArgNames
readOutArrayLengths callable nameMap
result <- convertResult nameMap
pps <- convertOut nameMap
freeCallCallbacks callable nameMap
forM_ (args callable) touchInArg
mapM_ line =<< freeInArgs callable nameMap
returnResult result pps
line " ) (do"
indent $ do
freeCallCallbacks callable nameMap
actions <- freeInArgsOnError callable nameMap
case actions of
[] -> line $ "return ()"
_ -> mapM_ line actions
line " )"
else do
invokeCFunction inArgNames
readOutArrayLengths callable nameMap
result <- convertResult nameMap
pps <- convertOut nameMap
freeCallCallbacks callable nameMap
forM_ (args callable) touchInArg
mapM_ line =<< freeInArgs callable nameMap
returnResult result pps
invokeCFunction argNames = do
let returnBind = case returnType callable of
TBasicType TVoid -> ""
_ -> if ignoreReturn
then "_ <- "
else "result <- "
maybeCatchGErrors = if throwsGError
then "propagateGError $ "
else ""
line $ returnBind ++ maybeCatchGErrors
++ T.unpack symbol ++ concatMap (" " ++) argNames
convertResult :: Map.Map String String -> ExcCodeGen String
convertResult nameMap =
if ignoreReturn || returnType callable == TBasicType TVoid
then return (error "convertResult: unreachable code reached, bug!")
else do
if returnMayBeNull callable
then do
line $ "maybeResult <- convertIfNonNull result $ \\result' -> do"
indent $ do
converted <- unwrappedConvertResult "result'"
line $ "return " ++ converted
return "maybeResult"
else do
nullable <- isNullable (returnType callable)
when nullable $
line $ "checkUnexpectedReturnNULL \"" ++ T.unpack symbol
++ "\" result"
unwrappedConvertResult "result"
where
unwrappedConvertResult rname = case returnType callable of
-- Arrays without length information are just passed
-- along.
TCArray False (-1) (-1) _ -> return rname
-- Not zero-terminated C arrays require knowledge of the
-- length, so we deal with them directly.
t@(TCArray False _ _ _) ->
convertOutCArray callable t rname nameMap
(returnTransfer callable)
t -> do
result <- convert rname $ fToH (returnType callable)
(returnTransfer callable)
freeContainerType (returnTransfer callable) t rname undefined
return result
convertOut :: Map.Map String String -> ExcCodeGen [String]
convertOut nameMap = do
-- Convert out parameters and result
forM hOutArgs $ \arg -> do
let name = escapeReserved $ argName arg
inName <- case Map.lookup name nameMap of
Just name' -> return name'
Nothing -> badIntroError $ "Parameter " ++ name ++ " not found!"
case argType arg of
-- Passed along as a raw pointer
TCArray False (-1) (-1) _ -> genConversion inName $ apply $ M "peek"
t@(TCArray False _ _ _) -> do
aname' <- genConversion inName $ apply $ M "peek"
let wrapArray a = convertOutCArray callable t a
nameMap (transfer arg)
wrapMaybe arg >>= bool
(wrapArray aname')
(do line $ "maybe" ++ ucFirst aname'
++ " <- convertIfNonNull " ++ aname'
++ " $ \\" ++ prime aname' ++ " -> do"
indent $ do
wrapped <- wrapArray (prime aname')
line $ "return " ++ wrapped
return $ "maybe" ++ ucFirst aname')
t -> do
weAlloc <- isJust <$> requiresAlloc t
peeked <- if weAlloc
then return inName
else genConversion inName $ apply $ M "peek"
-- If we alloc we always take control of the resulting
-- memory, otherwise we may leak.
let transfer' = if weAlloc
then TransferEverything
else transfer arg
result <- do
let wrap ptr = convert ptr $ fToH (argType arg) transfer'
wrapMaybe arg >>= bool
(wrap peeked)
(do line $ "maybe" ++ ucFirst peeked
++ " <- convertIfNonNull " ++ peeked
++ " $ \\" ++ prime peeked ++ " -> do"
indent $ do
wrapped <- wrap (prime peeked)
line $ "return " ++ wrapped
return $ "maybe" ++ ucFirst peeked)
-- Free the memory associated with the out argument
freeContainerType transfer' t peeked undefined
return result
returnResult :: String -> [String] -> CodeGen ()
returnResult result pps =
if ignoreReturn || returnType callable == TBasicType TVoid
then case pps of
[] -> line "return ()"
(pp:[]) -> line $ "return " ++ pp
_ -> line $ "return (" ++ intercalate ", " pps ++ ")"
else case pps of
[] -> line $ "return " ++ result
_ -> line $ "return (" ++ intercalate ", " (result : pps) ++ ")"