-- C->Haskell Compiler: custom wrapper generator -- -- Author : Manuel M T Chakravarty -- Created: 5 February 2003 -- -- Copyright (c) 2004 Manuel M T Chakravarty -- -- This file is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- This file is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- --- DESCRIPTION --------------------------------------------------------------- -- -- This module implements the generation of a custom C file to wrap -- functions requiring marshalling of bare C structs to pointers. -- module C2HS.Gen.Wrapper ( genWrappers ) where import Control.Monad -- Language.C / Compiler Toolkit import Language.C.Syntax import Language.C.Pretty import Text.PrettyPrint.HughesPJ (render) import Language.C.Data.Node (undefNode) import Language.C.Data.Position import Language.C.Data.Ident (Ident(..), internalIdent) import Data.DList (DList) import qualified Data.DList as DL -- C->Haskell import C2HS.State (CST, raiseError, throwExc, catchExc, errorsPresent, showErrors, fatal) import C2HS.C.Trav (isPtrDeclr) -- friends import C2HS.Gen.Monad (Wrapper(..)) -- | Generate a custom C wrapper from a CHS binding module for -- functions that require marshalling of bare C structs. -- genWrappers :: [Wrapper] -> CST s [String] genWrappers ws = do wraps <- mapM genWrapper (reverse ws) `ifWrapExc` return [] errs <- errorsPresent if errs then do errmsgs <- showErrors fatal ("Errors during generation of C wrappers:\n\n" ++ errmsgs) else do return $ DL.toList . DL.concat $ wraps -- | Process a single fragment. -- genWrapper :: Wrapper -> CST s (DList String) genWrapper (Wrapper wfn ofn (CDecl specs [(Just decl, _, _)] _) args (boolres, boolargs) pos) = do let renamed = rename (internalIdent wfn) decl wrapdecl <- fixArgs ofn pos args boolargs renamed let fspecs = if boolres then map replaceBoolSpec specs else specs expr <- callBody ofn pos args decl let body = CCompound [] [CBlockStmt (CReturn (Just expr) undefNode)] undefNode wrapfn = CFunDef fspecs wrapdecl [] body undefNode return $ DL.fromList [render (pretty wrapfn) ++ "\n"] genWrapper (Wrapper _ ofn _ _ _ pos) = internalWrapperErr pos ["genWrapper:" ++ ofn] rename :: Ident -> CDeclr -> CDeclr rename ide (CDeclr _ dds str attrs n) = CDeclr (Just ide) dds str attrs n fixArgs :: String -> Position -> [Bool] -> [Bool] -> CDeclr -> CST s CDeclr fixArgs ofn pos args bools (CDeclr ide fd str attrs n) = do fd' <- case fd of [] -> return [] f:fs -> do f' <- fixFunArgs ofn pos args bools f return $ f' : fs return $ CDeclr ide fd' str attrs n fixFunArgs :: String -> Position -> [Bool] -> [Bool] -> CDerivedDeclr -> CST s CDerivedDeclr fixFunArgs ofn pos args bools (CFunDeclr (Right (adecls, flg)) attrs n) = do adecls' <- zipWithM (fixDecl ofn pos) (zip3 args bools [1..]) adecls return $ CFunDeclr (Right (adecls', flg)) attrs n fixFunArgs ofn pos args bools cdecl = internalWrapperErr pos ["fixFunArgs:" ++ ofn, "args=" ++ show args, "bools=" ++ show bools, "cdecl=" ++ show cdecl] replaceBool :: CDecl -> CDecl replaceBool (CDecl spec ds n) = CDecl (map replaceBoolSpec spec) ds n replaceBoolSpec :: CDeclSpec -> CDeclSpec replaceBoolSpec (CTypeSpec (CBoolType tn)) = CTypeSpec (CCharType tn) replaceBoolSpec t = t fixDecl :: String -> Position -> (Bool, Bool, Int) -> CDecl -> CST s CDecl fixDecl _ _ (False, True, idx) d = return $ replaceBool $ fixEmpty d idx fixDecl _ _ (False, False, idx) d = return $ fixEmpty d idx fixDecl _ pos (True, _, idx) din = do let (CDecl specs [(Just decl, Nothing, Nothing)] n) = fixEmpty din idx decl' <- addPtr pos decl return $ CDecl specs [(Just decl', Nothing, Nothing)] n fixEmpty :: CDecl -> Int -> CDecl fixEmpty d@(CDecl _ [(Just _, Nothing, Nothing)] _) _ = d fixEmpty (CDecl ss [] n) idx = let d = CDeclr (Just $ internalIdent $ "c2hs__dummy_arg_" ++ show idx) [] Nothing [] n in CDecl ss [(Just d, Nothing, Nothing)] n addPtr :: Position -> CDeclr -> CST s CDeclr addPtr _ (CDeclr ide [] cs attrs n) = return $ CDeclr ide [CPtrDeclr [] n] cs attrs n addPtr pos cdecl = if isPtrDeclr cdecl then wrapperOnPointerErr pos else invalidWrapperErr pos callBody :: String -> Position -> [Bool] -> CDeclr -> CST s CExpr callBody fn pos args (CDeclr _ (fd:_) _ _ n) = do as <- zipWithM (makeArg pos) (zip args [1..]) (funArgs fd) return $ CCall (CVar (internalIdent fn) n) as n makeArg :: Position -> (Bool, Int) -> CDecl -> CST s CExpr makeArg _ (arg, _) (CDecl _ [(Just (CDeclr (Just i) _ _ _ _), _, _)] n) = return $ case arg of False -> CVar i n True -> CUnary CIndOp (CVar i n) n makeArg _ (arg, idx) (CDecl _ [] n) = let i = internalIdent $ "c2hs__dummy_arg_" ++ show idx in return $ case arg of False -> CVar i n True -> CUnary CIndOp (CVar i n) n makeArg pos (arg, idx) cdecl = internalWrapperErr pos ["makeArg:arg=" ++ show arg, "cdecl=" ++ show cdecl, "idx=" ++ show idx] funArgs :: CDerivedDeclr -> [CDecl] funArgs (CFunDeclr (Right (adecls, _)) _ _) = adecls throwWrapExc :: CST s a throwWrapExc = throwExc "wrapExc" "Error during wrapper generation" ifWrapExc :: CST s a -> CST s a -> CST s a ifWrapExc m handler = m `catchExc` ("wrapExc", const handler) raiseErrorWrapper :: Position -> [String] -> CST s a raiseErrorWrapper pos errs = raiseError pos errs >> throwWrapExc internalWrapperErr :: Position -> [String] -> CST s a internalWrapperErr pos msg = raiseErrorWrapper pos $ ["Internal wrapper error!", "Something went wrong generating a bare structure wrapper."] ++ msg wrapperOnPointerErr :: Position -> CST s a wrapperOnPointerErr pos = raiseErrorWrapper pos $ ["Bare structure wrapper error!", "Are you trying to put a wrapper on a pointer type?"] invalidWrapperErr :: Position -> CST s a invalidWrapperErr pos = raiseErrorWrapper pos $ ["Bare structure wrapper error!", "Invalid bare structure wrapper"]