% % (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       -- (base)name of output file
	 -> [HTopDecl]   -- Haskell decls to derive C code from.
	 -> String
cStubGen c_nm hs = inc_header ((showPPDoc (hCode hs)) [])
 where
  inc_header ls =
      -- avoid generating files containing just a singular #include.
    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}