% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Mar. 31th 2003 08:52 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Toplevel module for converting IDL into abstract Haskell code, ready for dumping out to a file. \begin{code} module CodeGen ( codeGen ) where import MarshallUtils import MarshallStruct import MarshallEnum import MarshallType import MarshallMethod import MarshallUnion import MarshallAbstract ( marshallAbstract ) import MarshallServ import MarshallCore import MarshallFun import MarshallJNI import MarshallJServ import Skeleton import qualified CustomAttributes import BasicTypes import Literal import AbstractH hiding (Type(..), Expr(..),CaseAlt(..)) import qualified AbstractH as Haskell ( HDecl(..), TyDecl(..), ConDecl(..), BangType(..) ) import AbsHUtils import MkImport ( mkImportLists ) import LibUtils import Opts ( optGenHeader, optNoExportList, optOneModulePerInterface, optServer, optUnparamedInterfacePointers, optSubtypedInterfacePointers, optExportAbstractly, optDualVtbl, optDon'tGenBinaryComInterfaces, optSkel, optIgnoreDispInterfaces, optNoLibIds, optIgnoreSourceIfaces, optNoEnumMagic, optHaskellToC, optJNI, optCorba, optIgnoreMethsUpto, optUseStdDispatch, optOutputHTo, optEnumsAsFlags, optGenNumInstance, optGenBitsInstance ) import PpAbstractH ( ppType, showAbstractH ) import PpCore ( showCore, ppDecl ) import CgMonad import CoreIDL import CoreUtils import Attribute import List ( partition, intersperse, isPrefixOf ) import Utils ( dropSuffix, trace, basename, split, splitdir, prefixDir, notNull ) import Maybe ( mapMaybe, isJust ) import Monad ( when ) import Env \end{code} External interface, convert a set of toplevel IDL declarations into their Haskell form. A translation unit can at the toplevel consist of: - one or more modules - one or more libraries - one or more (disp)interfaces/coclasses. IDL grouping declarations map onto Haskell modules as follows: - modules and libraries get their own Haskell module. - optionally, each (disp)interface/coclass can be put into a file of their own. (doing this eases the problem of namespace clashes, relying instead on the Haskell module system to operate satisfactorily..) \begin{code} codeGen :: (Either String String, Maybe String) {- User-supplied output name -} -> Env String [(Result, [Param])] {- environment carrying type isomorphic methods info -} -> Env String (Maybe Id) {- environment containing ifaces that should be ignored -} -> [Decl] -> ( [(String, Decl)] {- possible header file output -} , [(String, Bool, [HTopDecl])] {- Haskell output -} ) codeGen (o_fname, mname) iso_env iface_env decls = ( generateHeader top_module_name decls , (\ x -> if optSkel then x ++ cgSkeleton decls else x) $ liftOut (map (cGen iso_env iface_env) modules) ) where liftOut [] = [] liftOut (m@(nm, has_struct, ds) : ms) = case break isHMod ds of (ls,[x,HMod md@(HModule nm1 _ _ _ _)]) -> (nm, has_struct, ls ++ [x]) : (nm1 ++ ".hs", False, [HMod md]) : liftOut ms _ -> m : liftOut ms isHMod (HMod _) = True isHMod _ = False -- The set of modules to generate. If there's any leftovers at the -- top (i.e., stuff that appear outside a module/library/interface etc. grouping), -- we put these bits into the top_module. In the one-module-per-interface -- case, this means that for typedefs that appear inside interfaces will -- be lifted out and be generated with the outermost module (arguably they -- shouldn't be lifted out, but that's how things are organised at the moment.) modules = case mods_and_libs of [_] -> [one_module] -- make sure we emit everything. _ -> case rest of [] -> mods_and_libs _ -> top_module : mods_and_libs toStdOut = case o_fname of Right "-" -> True _ -> False -- use output filename to generate a plausible looking -- Haskell module name. (top_module_name, output_fname) = case mname of Just x -> (x, snd ofnames) _ -> ofnames where ofnames = case o_fname of Right "-" -> case mods_and_libs of ((_,m,_):_) -> (m, "-") _ -> ("Anon", "-") Left x -> let (_,base) = splitdir x y = mkHaskellTyConName (dropSuffix base) in (y, x) Right x -> case mods_and_libs of [(_,m,_)] -> let (dir,_) = splitdir x in (m, prefixDir dir (m ++ ".hs")) _ -> (mkModName x, x) mkModName x = mkHaskellTyConName (basename (dropSuffix x)) top_module = ( output_fname , top_module_name , mkTopModule (mkId top_module_name top_module_name Nothing [{-no attrs-}]) rest ) one_module = ( output_fname , top_module_name , mkTopModule (mkId top_module_name top_module_name Nothing [{-no attrs-}]) flattened_decls ) -- Hoist out some of these. Currently they all have to be at the top, -- we don't scan through the whole file looking for them. mkTopModule i (d@(HsLiteral _) : xs) = d : mkTopModule i xs mkTopModule i (d@(CInclude _) : xs) = d : mkTopModule i xs mkTopModule i (d@(CLiteral _) : xs) = d : mkTopModule i xs -- delayed addition of the attributes for the module itself. mkTopModule i ds@(d@Module{} : _) = [Module i{idAttributes=idAttributes (declId d)} ds] mkTopModule i ds@(d@Library{} : _) = [Module i{idAttributes=idAttributes (declId d)} ds] mkTopModule i ds = [Module i ds] mods_and_libs = map ( \ d -> let (x,y) = mkModuleLibName d in (x, y, [d])) mods_and_libs' (mods_and_libs', rest) = partition inSeparateHaskellModule flattened_decls flattened_decls = flattenDecls decls mkModuleLibName d | toStdOut = ("-", hnm) | otherwise = (hnm ++ ".hs", hnm) where hnm = mkHaskellTyConName nm nm_raw = idName (declId d) nm | optServer = nm_raw ++ "Proxy" | otherwise = nm_raw \end{code} \begin{code} cGen :: Env String [(Result, [Param])] -> Env String (Maybe Id) -> (String, String, [Decl]) -> (String, Bool, [HTopDecl]) cGen iso_env iface_env (oname, mod_name, ds) = (oname, flg, ds') where (ds',flg) = foldr mkHTop ([],False) ds mkHTop d (acc, has_structs) = case d of Library i ms -> case (runCgM iso_env iface_env (withDeclName (idName i) (cgLibrary i ms))) of (decl, expo, imps, flg1, has_prim) -> let qual_imps = mkImportLists mod_name (getHsImports i) [decl] real_imps = qual_imps ++ imps in (modDecl i has_prim [decl] real_imps expo : acc, has_structs || flg1) DispInterface i _ _ _ -> case (runCgM iso_env iface_env (withIfaceDeclName (idName i) (cgDecl d))) of (decl, expo, imps, flg1, has_prim) -> let qual_imps = mkImportLists mod_name (getHsImports i) [decl] real_imps = qual_imps ++ imps in (modDecl i has_prim [decl] real_imps expo : acc, has_structs || flg1) Interface i is_ref inherit _ | not is_ref -> case (runCgM iso_env iface_env (withIfaceDeclName (idName i) (cgDecl d))) of (decl, expo, imps, flg1, has_prim) -> let qual_imps = mkImportLists mod_name (getHsImports i) [decl] real_imps = qual_imps ++ imps attrs = idAttributes i iface_deps = filterAttributes attrs ["depender"] new_attrs = filterOutAttributes attrs ["depender"] i' = i{idAttributes=new_attrs} (acc', has_structs') | not optOneModulePerInterface || null iface_deps = (acc, has_structs) | otherwise = case mkHTop (Interface i' False inherit []) (acc, has_structs) of (HMod (HModule nm a b c d1) : ls, e) -> (HMod (HModule (nm ++ "Ty") a b c d1) : ls , e) x -> x in (modDecl i has_prim [decl] real_imps expo : acc', has_structs' || flg1) | otherwise -> (acc, has_structs) CoClass{} -> case (runCgM iso_env iface_env (cgDecl d)) of (decl, expo, imps, flg1, has_prim) -> let qual_imps = mkImportLists mod_name (getHsImports (declId d)) [decl] real_imps = qual_imps ++ imps in if isEmptyDecl decl then (acc, has_structs) else (hModule mod_name has_prim (map (\ (x,_,y) -> hExport x y) expo) (map (\ (x,y,z) -> hImport x y z) real_imps) decl : acc, has_structs || flg1) Module i ms -> case (runCgM iso_env iface_env (withDeclName (idName i) (cgModule i ms))) of (decl, expo,imps,flg1, has_prim) -> let qual_imps = mkImportLists mod_name (getHsImports i) [decl] real_imps = qual_imps ++ imps in ( modDecl (i{idName=mod_name}) has_prim [decl] real_imps expo : acc , has_structs || flg1 ) HsLiteral s -> (hMeta s : acc, has_structs) CLiteral s -> (cMeta s : acc, has_structs) CInclude s -> (hInclude s : acc, has_structs) _ -> error ("Odd decl: " ++ showCore (ppDecl d)) modDecl _ mflg decls imps expo | optNoExportList = hModule mod_name mflg (map (\ (x,_,y) -> hExport x y) $ filter (\ (_,x,_) -> x) expo) (map (\ (x,y,z) -> hImport x y z) imps) (andDecls decls) | otherwise = hModule mod_name mflg (map (\ (x,_,y) -> hExport x y) expo) (map (\ (x,y,z) -> hImport x y z) imps) (andDecls decls) \end{code} From the .idl input, we optionally generate C header file information. This is useful when the master copy of the .h file is the .idl specification (or, you don't have the header file already available.) \begin{code} generateHeader :: String -> [Decl] -> [(String, Decl)] generateHeader o_fname decls | not optGenHeader = [] | otherwise = case optOutputHTo of (x:_) -> let nm = dropSuffix x in [(oname, Module (mkId nm nm Nothing []) decls)] _ -> case (concatMap mkHeaderDecls decls) of [] -> [(oname, Module (mkId oname oname Nothing []) decls)] [(_,s)] -> [(oname, s)] ls -> ls where oname = case optOutputHTo of (x:_) -> x _ -> case o_fname of "-" -> "-" _ -> dropSuffix o_fname ++ ".h" mkHeaderNm i = mkHaskellTyConName (idName i) ++ ".h" mkHeaderDecls d = case d of Module i _ -> [(mkHeaderNm i, d)] Interface i _ _ _ -> [(mkHeaderNm i, d)] DispInterface i _ _ _ -> [(mkHeaderNm i, d)] Library i _ -> [(mkHeaderNm i, d)] _ -> [] \end{code} Generating code for the various Core IDL declarations: \begin{code} cgDecl :: Decl -> CgM HDecl cgDecl d = case d of Typedef n t _ -> cgTypedef n t Constant i t o_t e -> cgConstant i t o_t e Interface i _ inherit decls -> withIfaceInherit (map fst inherit) $ hoistInClass (idName i) $ \ mb_cls -> do cls_d <- {- Check to see if we should include a CLSID declaration as well. Do this in the case where we've got interface _A { ... }; coclass A { interface _A; }; and A has the only use of _A. Useful in one-module-per-interface mode, as it avoids creating a (v simple) module for the coclass. -} case mb_cls of Nothing -> return emptyDecl Just ci | notNull deps -> return emptyDecl | otherwise -> do let ci' | idName i `isPrefixOf` idName ci = ci{idName=idName i} | otherwise = ci ud <- setInterfaceFlag (ComIDispatch False) (uuidDecl ci' [] Clsid) return (infoHeader (CoClass ci' [CoClassInterface i Nothing]) `andDecl` ud) forClient <- getClientFlag let is_source = hasSourceAttribute (idAttributes i) setSourceIfaceFlag is_source $ do dserv <- if (is_source && optIgnoreSourceIfaces) then return emptyDecl else if (is_source && forClient) || (not forClient && not is_source) then marshallServ i inherit decls else cgInterface i inherit decls return (cls_d `andDecl` dserv) where deps = filterAttributes (idAttributes i) ["depender"] Method i cc res ps offs -> do forClient <- getClientFlag is_source <- getSourceIfaceFlag if (is_source && forClient) || (not forClient && not is_source) then do k <- getInterfaceFlag case k of StdFFI -> getDeclName $ \ nm -> marshallFun (Just nm) i (FunTy cc res ps) _ | optJNI -> cgJServMethod i res ps | otherwise -> do isInDisp <- isInDispInterface cgServMethod i res ps is_source (optUseStdDispatch && isInDisp) else if optJNI then cgJNIMethod i res ps else cgMethod i cc res ps offs Nothing Property i ty _ s g -> cgProperty i ty s g HsLiteral str -> return (Haskell str) CLiteral str -> return (CCode str) CInclude fname -> return (Include fname) DispInterface i ii ps ms -> cgDispInterface i ii ps ms CoClass i mems -> cgCoClass i mems Library i decls -> cgLibrary i decls Module i ds -> cgModule i ds _ -> return emptyDecl \end{code} % % Typedefs