fficxx-0.7.0.0: src/FFICXX/Generate/Dependency.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module FFICXX.Generate.Dependency where
-- fficxx generates one module per one C++ class, and C++ class depends on other classes,
-- so we need to import other modules corresponding to C++ classes in the dependency list.
-- Calculating the import list from dependency graph is what this module does.
-- Previously, we have only `Class` type, but added `TemplateClass` recently. Therefore
-- we have to calculate dependency graph for both types of classes. So we needed to change
-- `Class` to `Either TemplateClass Class` in many of routines that calculates module import
-- list.
-- `Dep4Func` contains a list of classes (both ordinary and template types) that is needed
-- for the definition of a member function.
-- The goal of `extractClassDep...` functions are to extract Dep4Func, and from the definition
-- of a class or a template class, we get a list of `Dep4Func`s and then we deduplicate the
-- dependency class list and finally get the import list for the module corresponding to
-- a given class.
import Data.Bifunctor (bimap)
import Data.Either (rights)
import Data.Function (on)
import qualified Data.HashMap.Strict as HM
import qualified Data.List as L (find, foldl', nub, nubBy)
import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
import FFICXX.Generate.Name
( ffiClassName,
getClassModuleBase,
getTClassModuleBase,
hsClassName,
)
import FFICXX.Generate.Type.Cabal
( AddCInc,
AddCSrc,
CabalName (..),
cabal_cheaderprefix,
cabal_pkgname,
unCabalName,
)
import FFICXX.Generate.Type.Class
( Arg (..),
CPPTypes (..),
Class (..),
DaughterMap,
Function (..),
TLOrdinary (..),
TLTemplate (..),
TemplateAppInfo (..),
TemplateArgType (TArg_Class),
TemplateClass (..),
TemplateFunction (..),
TemplateMemberFunction (..),
TopLevel (..),
Types (..),
Variable (unVariable),
argsFromOpExp,
filterTLOrdinary,
virtualFuncs,
)
import FFICXX.Generate.Type.Config
( ModuleUnit (..),
ModuleUnitImports (..),
ModuleUnitMap (..),
emptyModuleUnitImports,
)
import FFICXX.Generate.Type.Module
( ClassImportHeader (..),
ClassModule (..),
ClassSubmoduleType (..),
PackageConfig (..),
TemplateClassImportHeader (..),
TemplateClassModule (..),
TemplateClassSubmoduleType (..),
TopLevelImportHeader (..),
UClassSubmodule,
)
import FFICXX.Runtime.CodeGen.Cxx (HeaderName (..))
import System.FilePath ((<.>))
-- utility functions
getcabal = either tclass_cabal class_cabal
getparents = either (const []) (map Right . class_parents)
-- TODO: replace tclass_name with appropriate FFI name when supported.
getFFIName = either tclass_name ffiClassName
getPkgName :: Either TemplateClass Class -> CabalName
getPkgName = cabal_pkgname . getcabal
-- |
extractClassFromType :: Types -> [Either TemplateClass Class]
extractClassFromType Void = []
extractClassFromType SelfType = []
extractClassFromType (CT _ _) = []
extractClassFromType (CPT (CPTClass c) _) = [Right c]
extractClassFromType (CPT (CPTClassRef c) _) = [Right c]
extractClassFromType (CPT (CPTClassCopy c) _) = [Right c]
extractClassFromType (CPT (CPTClassMove c) _) = [Right c]
extractClassFromType (TemplateApp (TemplateAppInfo t ps _)) =
Left t : (map Right $ mapMaybe (\case TArg_Class c -> Just c; _ -> Nothing) ps)
extractClassFromType (TemplateAppRef (TemplateAppInfo t ps _)) =
Left t : (map Right $ mapMaybe (\case TArg_Class c -> Just c; _ -> Nothing) ps)
extractClassFromType (TemplateAppMove (TemplateAppInfo t ps _)) =
Left t : (map Right $ mapMaybe (\case TArg_Class c -> Just c; _ -> Nothing) ps)
extractClassFromType (TemplateType t) = [Left t]
extractClassFromType (TemplateParam _) = []
extractClassFromType (TemplateParamPointer _) = []
classFromArg :: Arg -> [Either TemplateClass Class]
classFromArg = extractClassFromType . arg_type
class_allparents :: Class -> [Class]
class_allparents c =
let ps = class_parents c
in if null ps
then []
else L.nub (ps <> (concatMap class_allparents ps))
-- | Daughter map not including itself
mkDaughterMap :: [Class] -> DaughterMap
mkDaughterMap = foldl mkDaughterMapWorker M.empty
where
mkDaughterMapWorker m c =
let ps = map getClassModuleBase (class_allparents c)
in foldl (addmeToYourDaughterList c) m ps
addmeToYourDaughterList c m p =
let f Nothing = Just [c]
f (Just cs) = Just (c : cs)
in M.alter f p m
-- | Daughter Map including itself as a daughter
mkDaughterSelfMap :: [Class] -> DaughterMap
mkDaughterSelfMap = L.foldl' worker M.empty
where
worker m c =
let ps = map getClassModuleBase (c : class_allparents c)
in foldl (addToList c) m ps
addToList c m p =
let f Nothing = Just [c]
f (Just cs) = Just (c : cs)
in M.alter f p m
-- | class dependency for a given function
data Dep4Func = Dep4Func
{ returnDependency :: [Either TemplateClass Class],
argumentDependency :: [Either TemplateClass Class]
}
-- |
extractClassDep :: Function -> Dep4Func
extractClassDep (Constructor args _) =
Dep4Func [] (concatMap classFromArg args)
extractClassDep (Virtual ret _ args _) =
Dep4Func (extractClassFromType ret) (concatMap classFromArg args)
extractClassDep (NonVirtual ret _ args _) =
Dep4Func (extractClassFromType ret) (concatMap classFromArg args)
extractClassDep (Static ret _ args _) =
Dep4Func (extractClassFromType ret) (concatMap classFromArg args)
extractClassDep (Destructor _) =
Dep4Func [] []
-- |
extractClassDepForTmplFun :: TemplateFunction -> Dep4Func
extractClassDepForTmplFun (TFun ret _ _ args) =
Dep4Func (extractClassFromType ret) (concatMap classFromArg args)
extractClassDepForTmplFun (TFunNew args _) =
Dep4Func [] (concatMap classFromArg args)
extractClassDepForTmplFun TFunDelete =
Dep4Func [] []
extractClassDepForTmplFun (TFunOp ret _ e) =
Dep4Func (extractClassFromType ret) (concatMap classFromArg $ argsFromOpExp e)
-- |
extractClassDep4TmplMemberFun :: TemplateMemberFunction -> Dep4Func
extractClassDep4TmplMemberFun (TemplateMemberFunction {..}) =
Dep4Func (extractClassFromType tmf_ret) (concatMap classFromArg tmf_args)
-- |
extractClassDepForTLOrdinary :: TLOrdinary -> Dep4Func
extractClassDepForTLOrdinary f =
Dep4Func (extractClassFromType ret) (concatMap (extractClassFromType . arg_type) args)
where
ret = case f of
TopLevelFunction {..} -> toplevelfunc_ret
TopLevelVariable {..} -> toplevelvar_ret
args = case f of
TopLevelFunction {..} -> toplevelfunc_args
TopLevelVariable {} -> []
-- |
extractClassDepForTLTemplate :: TLTemplate -> Dep4Func
extractClassDepForTLTemplate f =
Dep4Func (extractClassFromType ret) (concatMap (extractClassFromType . arg_type) args)
where
ret = topleveltfunc_ret f
args = topleveltfunc_args f
mkDepFFI :: Class -> [UClassSubmodule]
mkDepFFI cls =
let ps = map Right (class_allparents cls)
alldeps' = concatMap go ps <> go (Right cls)
depSelf = Right (CSTRawType, cls)
in depSelf : (fmap (bimap (TCSTTemplate,) (CSTRawType,)) $ L.nub $ filter (/= Right cls) alldeps')
where
go (Right c) =
let fs = class_funcs c
vs = class_vars c
tmfs = class_tmpl_funcs c
in concatMap (returnDependency . extractClassDep) fs
<> concatMap (argumentDependency . extractClassDep) fs
<> concatMap (classFromArg . unVariable) vs
<> concatMap (returnDependency . extractClassDep4TmplMemberFun) tmfs
<> concatMap (argumentDependency . extractClassDep4TmplMemberFun) tmfs
go (Left t) =
let fs = tclass_funcs t
in concatMap (returnDependency . extractClassDepForTmplFun) fs
<> concatMap (argumentDependency . extractClassDepForTmplFun) fs
-- For raws:
-- NOTE: Q: Why returnDependency for RawTypes?
-- A: Difference between argument and return:
-- for a member function f,
-- we have (f :: (IA a, IB b) => a -> b -> IO C
-- return class is concrete and argument class is constraint.
calculateDependency :: UClassSubmodule -> [UClassSubmodule]
calculateDependency (Left (typ, tcl)) = raws <> inplaces
where
raws' =
L.nub $
filter (/= Left tcl) $
concatMap (returnDependency . extractClassDepForTmplFun) $
tclass_funcs tcl
raws =
case typ of
TCSTTemplate ->
fmap (bimap (TCSTTemplate,) (CSTRawType,)) raws'
TCSTTH ->
concatMap
( \case
Left t -> [Left (TCSTTemplate, t)]
Right c -> fmap (Right . (,c)) [CSTRawType, CSTCast, CSTInterface]
)
raws'
inplaces =
let fs = tclass_funcs tcl
in fmap (bimap (TCSTTemplate,) (CSTInterface,)) $
L.nub $
filter (`isInSamePackageButNotInheritedBy` Left tcl) $
concatMap (argumentDependency . extractClassDepForTmplFun) fs
calculateDependency (Right (CSTRawType, _)) = []
calculateDependency (Right (CSTFFI, cls)) = mkDepFFI cls
calculateDependency (Right (CSTInterface, cls)) =
let retDepClasses =
concatMap (returnDependency . extractClassDep) (virtualFuncs $ class_funcs cls)
++ concatMap (returnDependency . extractClassDep4TmplMemberFun) (class_tmpl_funcs cls)
argDepClasses =
concatMap (argumentDependency . extractClassDep) (virtualFuncs $ class_funcs cls)
++ concatMap (argumentDependency . extractClassDep4TmplMemberFun) (class_tmpl_funcs cls)
rawSelf = Right (CSTRawType, cls)
raws =
fmap (bimap (TCSTTemplate,) (CSTRawType,)) $ L.nub $ filter (/= Right cls) retDepClasses
exts =
let extclasses =
filter (`isNotInSamePackageWith` Right cls) argDepClasses
parents = map Right (class_parents cls)
in fmap (bimap (TCSTTemplate,) (CSTInterface,)) $ L.nub (parents <> extclasses)
inplaces =
fmap (bimap (TCSTTemplate,) (CSTInterface,)) $
L.nub $
filter (`isInSamePackageButNotInheritedBy` Right cls) $ argDepClasses
in rawSelf : (raws ++ exts ++ inplaces)
calculateDependency (Right (CSTCast, cls)) = [Right (CSTRawType, cls), Right (CSTInterface, cls)]
calculateDependency (Right (CSTImplementation, cls)) =
let depsSelf =
[ Right (CSTRawType, cls),
Right (CSTFFI, cls),
Right (CSTInterface, cls),
Right (CSTCast, cls)
]
dsFFI = fmap (bimap snd snd) $ mkDepFFI cls
dsParents = L.nub $ map Right $ class_allparents cls
dsNonParents = filter (not . (flip elem dsParents)) dsFFI
deps =
concatMap
( \case
Left t -> [Left (TCSTTemplate, t)]
Right c ->
[ Right (CSTRawType, c),
Right (CSTCast, c),
Right (CSTInterface, c)
]
)
(dsNonParents <> dsParents)
in depsSelf <> deps
-- |
isNotInSamePackageWith ::
Either TemplateClass Class ->
Either TemplateClass Class ->
Bool
isNotInSamePackageWith x y = (x /= y) && (getPkgName x /= getPkgName y)
-- x is in the sam
isInSamePackageButNotInheritedBy ::
-- | y
Either TemplateClass Class ->
-- | x
Either TemplateClass Class ->
Bool
isInSamePackageButNotInheritedBy x y =
x /= y && not (x `elem` getparents y) && (getPkgName x == getPkgName y)
-- |
mkModuleDepCpp :: Either TemplateClass Class -> [Either TemplateClass Class]
mkModuleDepCpp y@(Right c) =
let fs = class_funcs c
vs = class_vars c
tmfs = class_tmpl_funcs c
in L.nub . filter (/= y) $
concatMap (returnDependency . extractClassDep) fs
<> concatMap (argumentDependency . extractClassDep) fs
<> concatMap (classFromArg . unVariable) vs
<> concatMap (returnDependency . extractClassDep4TmplMemberFun) tmfs
<> concatMap (argumentDependency . extractClassDep4TmplMemberFun) tmfs
<> getparents y
mkModuleDepCpp y@(Left t) =
let fs = tclass_funcs t
in L.nub . filter (/= y) $
concatMap (returnDependency . extractClassDepForTmplFun) fs
<> concatMap (argumentDependency . extractClassDepForTmplFun) fs
<> getparents y
-- | Find module-level dependency per each toplevel function/template function.
mkTopLevelDep :: TopLevel -> [UClassSubmodule]
mkTopLevelDep (TLOrdinary f) =
let dep4func = extractClassDepForTLOrdinary f
allDeps = returnDependency dep4func ++ argumentDependency dep4func
mkTags (Left tcl) = [Left (TCSTTemplate, tcl)]
mkTags (Right cls) = fmap (Right . (,cls)) [CSTRawType, CSTCast, CSTInterface]
in concatMap mkTags allDeps
mkTopLevelDep (TLTemplate f) =
let dep4func = extractClassDepForTLTemplate f
allDeps = returnDependency dep4func ++ argumentDependency dep4func
mkTags (Left tcl) = [Left (TCSTTemplate, tcl)]
mkTags (Right cls) = fmap (Right . (,cls)) [CSTRawType, CSTCast, CSTInterface]
in concatMap mkTags allDeps
-- |
mkClassModule ::
(ModuleUnit -> ModuleUnitImports) ->
[(String, [String])] ->
Class ->
ClassModule
mkClassModule getImports extra c =
ClassModule
{ cmModule = getClassModuleBase c,
cmCIH = mkCIH getImports c,
cmImportedSubmodulesForInterface = calculateDependency $ Right (CSTInterface, c),
cmImportedSubmodulesForFFI = calculateDependency $ Right (CSTFFI, c),
cmImportedSubmodulesForCast = calculateDependency $ Right (CSTCast, c),
cmImportedSubmodulesForImplementation = calculateDependency $ Right (CSTImplementation, c),
cmExtraImport = fromMaybe [] (lookup (class_name c) extra)
}
-- |
findModuleUnitImports :: ModuleUnitMap -> ModuleUnit -> ModuleUnitImports
findModuleUnitImports m u =
fromMaybe emptyModuleUnitImports (HM.lookup u (unModuleUnitMap m))
-- |
mkTCM ::
TemplateClassImportHeader ->
TemplateClassModule
mkTCM tcih =
let t = tcihTClass tcih
in TCM (getTClassModuleBase t) tcih
-- |
mkPackageConfig ::
-- | (package name,getImports)
(CabalName, ModuleUnit -> ModuleUnitImports) ->
([Class], [TopLevel], [TemplateClassImportHeader], [(String, [String])]) ->
[AddCInc] ->
[AddCSrc] ->
PackageConfig
mkPackageConfig (pkgname, getImports) (cs, fs, ts, extra) acincs acsrcs =
let ms = map (mkClassModule getImports extra) cs
cmpfunc x y = class_name (cihClass x) == class_name (cihClass y)
cihs = L.nubBy cmpfunc (map cmCIH ms)
--
tih = mkTIH pkgname getImports cihs fs
tcms = map mkTCM ts
tcihs = map tcmTCIH tcms
in PkgConfig
{ pcfg_classModules = ms,
pcfg_classImportHeaders = cihs,
pcfg_topLevelImportHeader = tih,
pcfg_templateClassModules = tcms,
pcfg_templateClassImportHeaders = tcihs,
pcfg_additional_c_incs = acincs,
pcfg_additional_c_srcs = acsrcs
}
-- |
mkPkgHeaderFileName :: Class -> HeaderName
mkPkgHeaderFileName c =
HdrName
( (cabal_cheaderprefix . class_cabal) c
<> fst (hsClassName c)
<.> "h"
)
-- |
mkPkgCppFileName :: Class -> String
mkPkgCppFileName c =
(cabal_cheaderprefix . class_cabal) c
<> fst (hsClassName c)
<.> "cpp"
-- |
mkPkgIncludeHeadersInH :: Class -> [HeaderName]
mkPkgIncludeHeadersInH c =
let pkgname = (cabal_pkgname . class_cabal) c
extclasses = filter ((/= pkgname) . getPkgName) . mkModuleDepCpp $ Right c
extheaders = L.nub . map ((<> "Type.h") . unCabalName . getPkgName) $ extclasses
in map mkPkgHeaderFileName (class_allparents c) <> map HdrName extheaders
-- |
mkPkgIncludeHeadersInCPP :: Class -> [HeaderName]
mkPkgIncludeHeadersInCPP = map mkPkgHeaderFileName . rights . mkModuleDepCpp . Right
-- |
mkCIH ::
-- | (mk namespace and include headers)
(ModuleUnit -> ModuleUnitImports) ->
Class ->
ClassImportHeader
mkCIH getImports c =
ClassImportHeader
{ cihClass = c,
cihSelfHeader = mkPkgHeaderFileName c,
cihNamespace = (muimports_namespaces . getImports . MU_Class . class_name) c,
cihSelfCpp = mkPkgCppFileName c,
cihImportedClasses = mkModuleDepCpp (Right c),
cihIncludedHPkgHeadersInH = mkPkgIncludeHeadersInH c,
cihIncludedHPkgHeadersInCPP = mkPkgIncludeHeadersInCPP c,
cihIncludedCPkgHeaders = (muimports_headers . getImports . MU_Class . class_name) c
}
-- | for top-level
mkTIH ::
CabalName ->
(ModuleUnit -> ModuleUnitImports) ->
[ClassImportHeader] ->
[TopLevel] ->
TopLevelImportHeader
mkTIH pkgname getImports cihs fs =
let ofs = filterTLOrdinary fs
tl_cs1 = concatMap (argumentDependency . extractClassDepForTLOrdinary) ofs
tl_cs2 = concatMap (returnDependency . extractClassDepForTLOrdinary) ofs
tl_cs = L.nubBy ((==) `on` either tclass_name ffiClassName) (tl_cs1 <> tl_cs2)
-- NOTE: Select only class dependencies in the current package.
-- TODO: This is clearly not a good impl. we need to look into this again
-- after reconsidering multi-package generation.
tl_cihs = catMaybes (foldr fn [] tl_cs)
where
fn c ys =
let y = L.find (\x -> (ffiClassName . cihClass) x == getFFIName c) cihs
in y : ys
-- NOTE: The remaining class dependencies outside the current package
extclasses = filter ((/= pkgname) . getPkgName) tl_cs
extheaders =
map HdrName $
L.nub $
map ((<> "Type.h") . unCabalName . getPkgName) extclasses
in TopLevelImportHeader
{ tihHeaderFileName = unCabalName pkgname <> "TopLevel",
tihClassDep = tl_cihs,
tihExtraClassDep = extclasses,
tihFuncs = fs,
tihNamespaces = muimports_namespaces (getImports MU_TopLevel),
tihExtraHeadersInH = extheaders,
tihExtraHeadersInCPP = muimports_headers (getImports MU_TopLevel)
}