module Data.GI.CodeGen.Transfer
( freeInArg
, freeInArgOnError
, freeContainerType
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>), (<*>))
#endif
import Control.Monad (when)
import Data.Monoid ((<>))
import Data.Text (Text)
import Data.GI.CodeGen.API
import Data.GI.CodeGen.Code
import Data.GI.CodeGen.Conversions
import Data.GI.CodeGen.GObject
import Data.GI.CodeGen.Type
import Data.GI.CodeGen.Util
basicFreeFn :: Type -> Maybe Text
basicFreeFn (TBasicType TUTF8) = Just "freeMem"
basicFreeFn (TBasicType TFileName) = Just "freeMem"
basicFreeFn (TBasicType _) = Nothing
basicFreeFn (TInterface _) = Nothing
basicFreeFn (TCArray False (-1) (-1) (TBasicType TUInt8)) = Nothing
basicFreeFn (TCArray{}) = Just "freeMem"
basicFreeFn (TGArray _) = Just "unrefGArray"
basicFreeFn (TPtrArray _) = Just "unrefPtrArray"
basicFreeFn (TByteArray) = Just "unrefGByteArray"
basicFreeFn (TGList _) = Just "g_list_free"
basicFreeFn (TGSList _) = Just "g_slist_free"
basicFreeFn (TGHash _ _) = Just "unrefGHashTable"
basicFreeFn (TError) = Nothing
basicFreeFn (TVariant) = Nothing
basicFreeFn (TParamSpec) = Nothing
basicFreeFnOnError :: Type -> Transfer -> CodeGen (Maybe Text)
basicFreeFnOnError (TBasicType TUTF8) _ = return $ Just "freeMem"
basicFreeFnOnError (TBasicType TFileName) _ = return $ Just "freeMem"
basicFreeFnOnError (TBasicType _) _ = return Nothing
basicFreeFnOnError TVariant transfer =
return $ if transfer == TransferEverything
then Just "unrefGVariant"
else Nothing
basicFreeFnOnError TParamSpec transfer =
return $ if transfer == TransferEverything
then Just "unrefGParamSpec"
else Nothing
basicFreeFnOnError t@(TInterface _) transfer = do
api <- findAPI t
case api of
Just (APIObject _) -> if transfer == TransferEverything
then do
isGO <- isGObject t
if isGO
then return $ Just "unrefObject"
else do
line "-- XXX Transfer a non-GObject object"
return Nothing
else return Nothing
Just (APIInterface _) -> if transfer == TransferEverything
then do
isGO <- isGObject t
if isGO
then return $ Just "unrefObject"
else do
line "-- XXX Transfer a non-GObject object"
return Nothing
else return Nothing
Just (APIUnion u) -> if transfer == TransferEverything
then if unionIsBoxed u
then return $ Just "freeBoxed"
else do
line "-- XXX Transfer a non-boxed union"
return Nothing
else return Nothing
Just (APIStruct s) -> if transfer == TransferEverything
then if structIsBoxed s
then return $ Just "freeBoxed"
else do
line "-- XXX Transfer a non-boxed struct"
return Nothing
else return Nothing
_ -> return Nothing
basicFreeFnOnError (TCArray False (-1) (-1) (TBasicType TUInt8)) _ = return Nothing
basicFreeFnOnError (TCArray{}) _ = return $ Just "freeMem"
basicFreeFnOnError (TGArray _) _ = return $ Just "unrefGArray"
basicFreeFnOnError (TPtrArray _) _ = return $ Just "unrefPtrArray"
basicFreeFnOnError (TByteArray) _ = return $ Just "unrefGByteArray"
basicFreeFnOnError (TGList _) _ = return $ Just "g_list_free"
basicFreeFnOnError (TGSList _) _ = return $ Just "g_slist_free"
basicFreeFnOnError (TGHash _ _) _ = return $ Just "unrefGHashTable"
basicFreeFnOnError (TError) _ = return Nothing
freeContainer :: Type -> Text -> CodeGen [Text]
freeContainer t label =
case basicFreeFn t of
Nothing -> return []
Just fn -> return [fn <> " " <> label]
freeElem :: Type -> Text -> Text -> ExcCodeGen Text
freeElem t label free =
case elementTypeAndMap t undefined of
Nothing -> return free
Just (TCArray False _ _ _, _) ->
badIntroError $ "Element type in container \"" <> label <>
"\" is an array of unknown length."
Just (innerType, mapFn) -> do
let elemFree = "freeElemOf" <> ucFirst label
fullyFree innerType (prime label) >>= \case
Nothing -> return $ free <> " e"
Just elemInnerFree -> do
line $ "let " <> elemFree <> " e = " <> mapFn <> " "
<> elemInnerFree <> " e >> " <> free <> " e"
return elemFree
fullyFree :: Type -> Text -> ExcCodeGen (Maybe Text)
fullyFree t label = case basicFreeFn t of
Nothing -> return Nothing
Just free -> Just <$> freeElem t label free
fullyFreeOnError :: Type -> Text -> Transfer -> ExcCodeGen (Maybe Text)
fullyFreeOnError t label transfer =
basicFreeFnOnError t transfer >>= \case
Nothing -> return Nothing
Just free -> Just <$> freeElem t label free
freeElements :: Type -> Text -> Text -> ExcCodeGen [Text]
freeElements t label len =
case elementTypeAndMap t len of
Nothing -> return []
Just (inner, mapFn) ->
fullyFree inner label >>= \case
Nothing -> return []
Just innerFree ->
return [mapFn <> " " <> innerFree <> " " <> label]
freeContainerType :: Transfer -> Type -> Text -> Text -> ExcCodeGen ()
freeContainerType transfer (TGHash _ _) label _ = freeGHashTable transfer label
freeContainerType transfer t label len = do
when (transfer == TransferEverything) $
mapM_ line =<< freeElements t label len
when (transfer /= TransferNothing) $
mapM_ line =<< freeContainer t label
freeGHashTable :: Transfer -> Text -> ExcCodeGen ()
freeGHashTable TransferNothing _ = return ()
freeGHashTable TransferContainer label =
notImplementedError $ "Hash table argument with transfer = Container? "
<> label
freeGHashTable TransferEverything label =
line $ "unrefGHashTable " <> label
freeElementsOnError :: Transfer -> Type -> Text -> Text ->
ExcCodeGen [Text]
freeElementsOnError transfer t label len =
case elementTypeAndMap t len of
Nothing -> return []
Just (inner, mapFn) ->
fullyFreeOnError inner label transfer >>= \case
Nothing -> return []
Just innerFree ->
return [mapFn <> " " <> innerFree <> " " <> label]
freeIn :: Transfer -> Type -> Text -> Text -> ExcCodeGen [Text]
freeIn transfer (TGHash _ _) label _ =
freeInGHashTable transfer label
freeIn transfer t label len =
case transfer of
TransferNothing -> (<>) <$> freeElements t label len <*> freeContainer t label
TransferContainer -> freeElements t label len
TransferEverything -> return []
freeInOnError :: Transfer -> Type -> Text -> Text -> ExcCodeGen [Text]
freeInOnError transfer (TGHash _ _) label _ =
freeInGHashTable transfer label
freeInOnError transfer t label len =
(<>) <$> freeElementsOnError transfer t label len
<*> freeContainer t label
freeInGHashTable :: Transfer -> Text -> ExcCodeGen [Text]
freeInGHashTable TransferEverything _ = return []
freeInGHashTable TransferContainer label =
notImplementedError $ "Hash table argument with TransferContainer? "
<> label
freeInGHashTable TransferNothing label = return ["unrefGHashTable " <> label]
freeOut :: Text -> CodeGen [Text]
freeOut label = return ["freeMem " <> label]
freeInArg :: Arg -> Text -> Text -> ExcCodeGen [Text]
freeInArg arg label len = do
if not (argCallerAllocates arg)
then case direction arg of
DirectionIn -> freeIn (transfer arg) (argType arg) label len
DirectionOut -> freeOut label
DirectionInout -> freeOut label
else return []
freeInArgOnError :: Arg -> Text -> Text -> ExcCodeGen [Text]
freeInArgOnError arg label len =
case direction arg of
DirectionIn -> freeInOnError (transfer arg) (argType arg) label len
DirectionOut -> freeOut label
DirectionInout ->
if argCallerAllocates arg
then freeInOnError (transfer arg) (argType arg) label len
else freeOut label