-----------------------------------------------------------------------------
--
-- Types for the Dynamic Linker
--
-- (c) The University of Glasgow 2019
--
-----------------------------------------------------------------------------

module GHC.Runtime.Linker.Types (
      DynLinker(..),
      PersistentLinkerState(..),
      Linkable(..),
      Unlinked(..),
      SptEntry(..)
    ) where

import GHC.Prelude             ( FilePath, String, show )
import Data.Time               ( UTCTime )
import Data.Maybe              ( Maybe )
import Control.Concurrent.MVar ( MVar )
import GHC.Unit                ( UnitId, Module )
import GHC.ByteCode.Types      ( ItblEnv, CompiledByteCode )
import GHC.Utils.Outputable
import GHC.Types.Var           ( Id )
import GHC.Fingerprint.Type    ( Fingerprint )
import GHC.Types.Name.Env      ( NameEnv )
import GHC.Types.Name          ( Name )
import GHCi.RemoteTypes        ( ForeignHValue )

type ClosureEnv = NameEnv (Name, ForeignHValue)

newtype DynLinker =
  DynLinker { DynLinker -> MVar (Maybe PersistentLinkerState)
dl_mpls :: MVar (Maybe PersistentLinkerState) }

data PersistentLinkerState
  = PersistentLinkerState {

       -- Current global mapping from Names to their true values
       PersistentLinkerState -> ClosureEnv
closure_env :: ClosureEnv,

       -- The current global mapping from RdrNames of DataCons to
       -- info table addresses.
       -- When a new Unlinked is linked into the running image, or an existing
       -- module in the image is replaced, the itbl_env must be updated
       -- appropriately.
       PersistentLinkerState -> ItblEnv
itbl_env    :: !ItblEnv,

       -- The currently loaded interpreted modules (home package)
       PersistentLinkerState -> [Linkable]
bcos_loaded :: ![Linkable],

       -- And the currently-loaded compiled modules (home package)
       PersistentLinkerState -> [Linkable]
objs_loaded :: ![Linkable],

       -- The currently-loaded packages; always object code
       -- Held, as usual, in dependency order; though I am not sure if
       -- that is really important
       PersistentLinkerState -> [UnitId]
pkgs_loaded :: ![UnitId],

       -- we need to remember the name of previous temporary DLL/.so
       -- libraries so we can link them (see #10322)
       PersistentLinkerState -> [(FilePath, FilePath)]
temp_sos :: ![(FilePath, String)] }

-- | Information we can use to dynamically link modules into the compiler
data Linkable = LM {
  Linkable -> UTCTime
linkableTime     :: UTCTime,          -- ^ Time at which this linkable was built
                                        -- (i.e. when the bytecodes were produced,
                                        --       or the mod date on the files)
  Linkable -> Module
linkableModule   :: Module,           -- ^ The linkable module itself
  Linkable -> [Unlinked]
linkableUnlinked :: [Unlinked]
    -- ^ Those files and chunks of code we have yet to link.
    --
    -- INVARIANT: A valid linkable always has at least one 'Unlinked' item.
    -- If this list is empty, the Linkable represents a fake linkable, which
    -- is generated in HscNothing mode to avoid recompiling modules.
    --
    -- ToDo: Do items get removed from this list when they get linked?
 }

instance Outputable Linkable where
  ppr :: Linkable -> SDoc
ppr (LM UTCTime
when_made Module
mod [Unlinked]
unlinkeds)
     = (FilePath -> SDoc
text FilePath
"LinkableM" SDoc -> SDoc -> SDoc
<+> SDoc -> SDoc
parens (FilePath -> SDoc
text (UTCTime -> FilePath
forall a. Show a => a -> FilePath
show UTCTime
when_made)) SDoc -> SDoc -> SDoc
<+> Module -> SDoc
forall a. Outputable a => a -> SDoc
ppr Module
mod)
       SDoc -> SDoc -> SDoc
$$ Int -> SDoc -> SDoc
nest Int
3 ([Unlinked] -> SDoc
forall a. Outputable a => a -> SDoc
ppr [Unlinked]
unlinkeds)

-- | Objects which have yet to be linked by the compiler
data Unlinked
  = DotO FilePath      -- ^ An object file (.o)
  | DotA FilePath      -- ^ Static archive file (.a)
  | DotDLL FilePath    -- ^ Dynamically linked library file (.so, .dll, .dylib)
  | BCOs CompiledByteCode
         [SptEntry]    -- ^ A byte-code object, lives only in memory. Also
                       -- carries some static pointer table entries which
                       -- should be loaded along with the BCOs.
                       -- See Note [Grant plan for static forms] in
                       -- "GHC.Iface.Tidy.StaticPtrTable".

instance Outputable Unlinked where
  ppr :: Unlinked -> SDoc
ppr (DotO FilePath
path)   = FilePath -> SDoc
text FilePath
"DotO" SDoc -> SDoc -> SDoc
<+> FilePath -> SDoc
text FilePath
path
  ppr (DotA FilePath
path)   = FilePath -> SDoc
text FilePath
"DotA" SDoc -> SDoc -> SDoc
<+> FilePath -> SDoc
text FilePath
path
  ppr (DotDLL FilePath
path) = FilePath -> SDoc
text FilePath
"DotDLL" SDoc -> SDoc -> SDoc
<+> FilePath -> SDoc
text FilePath
path
  ppr (BCOs CompiledByteCode
bcos [SptEntry]
spt) = FilePath -> SDoc
text FilePath
"BCOs" SDoc -> SDoc -> SDoc
<+> CompiledByteCode -> SDoc
forall a. Outputable a => a -> SDoc
ppr CompiledByteCode
bcos SDoc -> SDoc -> SDoc
<+> [SptEntry] -> SDoc
forall a. Outputable a => a -> SDoc
ppr [SptEntry]
spt

-- | An entry to be inserted into a module's static pointer table.
-- See Note [Grand plan for static forms] in "GHC.Iface.Tidy.StaticPtrTable".
data SptEntry = SptEntry Id Fingerprint

instance Outputable SptEntry where
  ppr :: SptEntry -> SDoc
ppr (SptEntry Id
id Fingerprint
fpr) = Id -> SDoc
forall a. Outputable a => a -> SDoc
ppr Id
id SDoc -> SDoc -> SDoc
<> SDoc
colon SDoc -> SDoc -> SDoc
<+> Fingerprint -> SDoc
forall a. Outputable a => a -> SDoc
ppr Fingerprint
fpr