%
% (c) 1999, sof
%
Generate C stubs to make up the difference between the
set of return types allowed by the GHC/Hugs FFI and C
(C lets you return structs/unions by value, the FFI doesn't).
\begin{code}
module CStubGen (cStubGen) where
import AbstractH
import AbsHUtils ( splitFunTys )
import PP
import BasicTypes
import Utils ( traceIf, dropSuffix )
import Opts ( optGenHeader, optVerbose, optOneModulePerInterface )
import List ( nub )
import Maybe ( isJust )
import Utils ( notNull )
\end{code}
\begin{code}
cStubGen :: String
-> [HTopDecl]
-> String
cStubGen c_nm hs = inc_header ((showPPDoc (hCode hs)) [])
where
inc_header ls =
case ls of
"" -> ls
_ | optGenHeader && not optOneModulePerInterface -> "#include "++ show (dropSuffix c_nm ++ ".h") ++ '\n':ls
| otherwise -> ls
\end{code}
The generated C code may contain bindings to DLLs that are linked
by ordinal (Win32 specific), so we keep track of which DLLs they
are and simply just inform the user that special steps needs to
be taken in order to have this work successfully (i.e., need to
get at the .a / .lib for that DLL or generate it from a .def file.)
\begin{code}
type CStubCode = PPDoc [String]
getDllEnv :: ([String] -> CStubCode) -> CStubCode
getDllEnv cont env = cont env env
addToDllEnv :: String -> CStubCode -> CStubCode
addToDllEnv nm cont env = cont (nm:env)
hCode :: [HTopDecl] -> CStubCode
hCode xs = whizz xs
where
whizz [] =
getDllEnv $ \ dlls ->
let dlls_real = nub (filter notNull dlls) in
traceIf (optVerbose && notNull dlls_real)
("\nStubs depend on entry points from the following DLLs/libraries:\n " ++
showList dlls_real (
"\nyou may need to adjust your command-line when compiling the stubs to" ++
"\ntake this into account.")) empty
whizz (HLit _ : ls) = whizz ls
whizz (CLit s : ls)
| not optGenHeader = text s $$ whizz ls
| otherwise = whizz ls
whizz (HInclude s : ls) = text "#include" <+> text (escapeString s) $$
whizz ls
whizz (HMod hm : ls) = hMod hm (whizz ls)
escapeString s@('"':_) = s
escapeString s@('<':_) = s
escapeString s = show s
hMod :: HModule -> CStubCode -> CStubCode
hMod (HModule _ _ _ _ d) cont = hDecl d cont
hDecl :: HDecl -> CStubCode -> CStubCode
hDecl (AndDecl d1 d2) cont = hDecl d1 (hDecl d2 cont)
hDecl (Primitive _ cc lspec nm ty needs_wrapper c_args c_res) cont
| not needs_a_wrapper = cont
| otherwise =
addToDllEnv dll_name $
tdefFun lspec cc c_args c_res $$
primHeader nm c_res c_args $$
lbrace $$
declResult $$
performCall False lspec ty c_args $$
pushResult c_res ty $$
rbrace $$
cont
where
declResult =
case snd c_res of
"void" -> empty
x -> text x <+> text "res" <> semi
needs_a_wrapper = needs_wrapper || isJust mb_ord
(dll_name, mb_ord, _, _) = lspec
tdefFun (_,Nothing,_,_) _ _ _ = empty
tdefFun (_,Just _,tnm,_) tcc args res =
text "extern" <+> text (snd res) <+> ppCallConv True tcc <+> text tnm <+>
ppTuple (map (text.snd) args) <> semi
hDecl (PrimCast cc nm ty needs_wrapper c_args c_res) cont
| not needs_wrapper = cont
| otherwise =
tdefFunTy ty nm cc c_args c_res $$
primHeader nm c_res c_args $$
lbrace $$
declResult $$
text (nm++"__funptr __funptr__;") $$
text ("__funptr__ = ("++nm++"__funptr)arg0;") $$
performCall True ("", Nothing, "__funptr__", Nothing) ty c_args $$
pushResult c_res ty $$
rbrace $$
cont
where
declResult =
case snd c_res of
"void" -> empty
x -> text x <+> text "res" <> semi
hDecl (Include s) cont = text ("#include " ++ s) $$ cont
hDecl (CCode s) cont
| not optGenHeader = text s $$ cont
| otherwise = cont
hDecl _ cont = cont
\end{code}
\begin{code}
primHeader :: Name -> (Bool,String) -> [(Bool,String)] -> CStubCode
primHeader nm res args =
text the_res <+> text nm <+>
ppTuple (zipWith (\ x n -> text (showTy x) <+> ppArg n) args [(0::Int)..])
where
the_res = showTy res
showTy :: (Bool,String) -> String
showTy (is_str,ty_str)
| is_str = ty_str ++ "*"
| otherwise = ty_str
ppArg :: Int -> CStubCode
ppArg n = text ("arg"++show n)
\end{code}
\begin{code}
performCall :: Bool -> LocSpec -> Type -> [(Bool,String)] -> CStubCode
performCall is_dyn (_, _, fun, _) ty c_args =
ppAssign res <> text fun <> ppTuple fun_args <> semi
where
(_, res) = splitFunTys ty
fun_args
| is_dyn = tail funArgs
| otherwise = funArgs
funArgs = zipWith funArg [0..] c_args
funArg n (is_struct, _)
| is_struct = char '*' <> ppArg n
| otherwise = ppArg n
ppAssign t
| noResultTy t = empty
| otherwise = text "res" <+> equals
\end{code}
\begin{code}
pushResult :: (Bool, String) -> Type -> CStubCode
pushResult (isStructTy, c_ty) ty = assignRes
where
(_, res) = splitFunTys ty
noResult = noResultTy res
assignRes
| noResult = empty
| otherwise = text "return" <+> parens (the_result) <> semi
the_result
| isStructTy = text "copyBytes" <> parens (
text "sizeof" <> parens (text c_ty) <>
text ", &res")
| otherwise = text "res"
\end{code}
\begin{code}
tdefFunTy :: Type -> Name -> CallConv -> [(Bool,String)] -> (Bool,String) -> CStubCode
tdefFunTy ty nm cc c_args (_,c_res) =
text "typedef" <+> ppResultTy <+>
parens ( ppCallConv True cc <+> char '*' <+>
text (nm++"__funptr")) <+>
ppTuple ppArgs <> semi
where
(_, res) = splitFunTys ty
ppResultTy
| noResult = text "void"
| otherwise = text c_res
ppArgs = zipWith pp_arg [1..] (tail c_args)
pp_arg n (_,t) = text t <+> ppArg n
noResult = noResultTy res
noResultTy :: Type -> Bool
noResultTy (TyApply (TyCon _) [TyCon tc]) = qName tc == "()"
noResultTy _ = False
\end{code}