{-# LANGUAGE RecordWildCards #-}

-- | Information attached to Breakpoints generated from Ticks
--
-- The breakpoint information stored in 'ModBreaks' is generated during
-- desugaring from the ticks annotating the source expressions.
--
-- This information can be queried per-breakpoint using the 'BreakpointId'
-- datatype, which indexes tick-level breakpoint information.
--
-- 'ModBreaks' and 'BreakpointId's are not to be confused with
-- 'InternalModBreaks' and 'InternalBreakId's. The latter are constructed
-- during bytecode generation and can be found in 'GHC.ByteCode.Breakpoints'.
--
-- See Note [Breakpoint identifiers]
module GHC.HsToCore.Breakpoints
  ( -- * ModBreaks
    mkModBreaks, ModBreaks(..)

    -- ** Re-exports BreakpointId
  , BreakpointId(..), BreakTickIndex
  ) where

import GHC.Prelude
import Data.Array

import GHC.HsToCore.Ticks (Tick (..))
import GHC.Data.SizedSeq
import GHC.Types.SrcLoc (SrcSpan)
import GHC.Types.Name (OccName)
import GHC.Types.Tickish (BreakTickIndex, BreakpointId(..))
import GHC.Unit.Module (Module)
import GHC.Utils.Outputable
import Data.List (intersperse)

--------------------------------------------------------------------------------
-- ModBreaks
--------------------------------------------------------------------------------

-- | All the information about the source-relevant breakpoints for a module
--
-- This information is constructed once during desugaring (with `mkModBreaks`)
-- from breakpoint ticks and fixed/unchanged from there on forward. It could be
-- exported as an abstract datatype because it should never be updated after
-- construction, only queried.
--
-- The arrays can be indexed using the int in the corresponding 'BreakpointId'
-- (i.e. the 'BreakpointId' whose 'Module' matches the 'Module' corresponding
-- to these 'ModBreaks') with the accessors 'modBreaks_locs', 'modBreaks_vars',
-- and 'modBreaks_decls'.
data ModBreaks
   = ModBreaks
   { ModBreaks -> Array BreakTickIndex SrcSpan
modBreaks_locs   :: !(Array BreakTickIndex SrcSpan)
        -- ^ An array giving the source span of each breakpoint.
   , ModBreaks -> Array BreakTickIndex [OccName]
modBreaks_vars   :: !(Array BreakTickIndex [OccName])
        -- ^ An array giving the names of the free variables at each breakpoint.
   , ModBreaks -> Array BreakTickIndex [String]
modBreaks_decls  :: !(Array BreakTickIndex [String])
        -- ^ An array giving the names of the declarations enclosing each breakpoint.
        -- See Note [Field modBreaks_decls]
   , ModBreaks -> Array BreakTickIndex (String, String)
modBreaks_ccs    :: !(Array BreakTickIndex (String, String))
        -- ^ Array pointing to cost centre info for each breakpoint;
        -- actual 'CostCentre' allocation is done at link-time.
   , ModBreaks -> Module
modBreaks_module :: !Module
        -- ^ The module to which this ModBreaks is associated.
        -- We also cache this here for internal sanity checks.
   }

-- | Initialize memory for breakpoint data that is shared between the bytecode
-- generator and the interpreter.
--
-- Since GHCi and the RTS need to interact with breakpoint data and the bytecode
-- generator needs to encode this information for each expression, the data is
-- allocated remotely in GHCi's address space and passed to the codegen as
-- foreign pointers.
mkModBreaks :: Bool {-^ Whether the interpreter is profiled and thus if we should include store a CCS array -}
            -> Module -> SizedSeq Tick -> ModBreaks
mkModBreaks :: Bool -> Module -> SizedSeq Tick -> ModBreaks
mkModBreaks Bool
interpreterProfiled Module
modl SizedSeq Tick
extendedMixEntries
  = let count :: BreakTickIndex
count = Word -> BreakTickIndex
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> BreakTickIndex) -> Word -> BreakTickIndex
forall a b. (a -> b) -> a -> b
$ SizedSeq Tick -> Word
forall a. SizedSeq a -> Word
sizeSS SizedSeq Tick
extendedMixEntries
        entries :: [Tick]
entries = SizedSeq Tick -> [Tick]
forall a. SizedSeq a -> [a]
ssElts SizedSeq Tick
extendedMixEntries
        locsTicks :: Array BreakTickIndex SrcSpan
locsTicks  = (BreakTickIndex, BreakTickIndex)
-> [SrcSpan] -> Array BreakTickIndex SrcSpan
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray (BreakTickIndex
0,BreakTickIndex
countBreakTickIndex -> BreakTickIndex -> BreakTickIndex
forall a. Num a => a -> a -> a
-BreakTickIndex
1) [ Tick -> SrcSpan
tick_loc  Tick
t | Tick
t <- [Tick]
entries ]
        varsTicks :: Array BreakTickIndex [OccName]
varsTicks  = (BreakTickIndex, BreakTickIndex)
-> [[OccName]] -> Array BreakTickIndex [OccName]
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray (BreakTickIndex
0,BreakTickIndex
countBreakTickIndex -> BreakTickIndex -> BreakTickIndex
forall a. Num a => a -> a -> a
-BreakTickIndex
1) [ Tick -> [OccName]
tick_ids  Tick
t | Tick
t <- [Tick]
entries ]
        declsTicks :: Array BreakTickIndex [String]
declsTicks = (BreakTickIndex, BreakTickIndex)
-> [[String]] -> Array BreakTickIndex [String]
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray (BreakTickIndex
0,BreakTickIndex
countBreakTickIndex -> BreakTickIndex -> BreakTickIndex
forall a. Num a => a -> a -> a
-BreakTickIndex
1) [ Tick -> [String]
tick_path Tick
t | Tick
t <- [Tick]
entries ]
        ccs :: Array BreakTickIndex (String, String)
ccs
          | Bool
interpreterProfiled =
              (BreakTickIndex, BreakTickIndex)
-> [(String, String)] -> Array BreakTickIndex (String, String)
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray
                (BreakTickIndex
0, BreakTickIndex
count BreakTickIndex -> BreakTickIndex -> BreakTickIndex
forall a. Num a => a -> a -> a
- BreakTickIndex
1)
                [ ( [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String -> [String] -> [String]
forall a. a -> [a] -> [a]
intersperse String
"." ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Tick -> [String]
tick_path Tick
t,
                    SDocContext -> SDoc -> String
renderWithContext SDocContext
defaultSDocContext (SDoc -> String) -> SDoc -> String
forall a b. (a -> b) -> a -> b
$ SrcSpan -> SDoc
forall a. Outputable a => a -> SDoc
ppr (SrcSpan -> SDoc) -> SrcSpan -> SDoc
forall a b. (a -> b) -> a -> b
$ Tick -> SrcSpan
tick_loc Tick
t
                  )
                | Tick
t <- [Tick]
entries
                ]
          | Bool
otherwise = (BreakTickIndex, BreakTickIndex)
-> [(String, String)] -> Array BreakTickIndex (String, String)
forall i e. Ix i => (i, i) -> [e] -> Array i e
listArray (BreakTickIndex
0, -BreakTickIndex
1) []
     in ModBreaks
      { modBreaks_locs :: Array BreakTickIndex SrcSpan
modBreaks_locs   = Array BreakTickIndex SrcSpan
locsTicks
      , modBreaks_vars :: Array BreakTickIndex [OccName]
modBreaks_vars   = Array BreakTickIndex [OccName]
varsTicks
      , modBreaks_decls :: Array BreakTickIndex [String]
modBreaks_decls  = Array BreakTickIndex [String]
declsTicks
      , modBreaks_ccs :: Array BreakTickIndex (String, String)
modBreaks_ccs    = Array BreakTickIndex (String, String)
ccs
      , modBreaks_module :: Module
modBreaks_module = Module
modl
      }

{-
Note [Field modBreaks_decls]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A value of eg ["foo", "bar", "baz"] in a `modBreaks_decls` field means:
The breakpoint is in the function called "baz" that is declared in a `let`
or `where` clause of a declaration called "bar", which itself is declared
in a `let` or `where` clause of the top-level function called "foo".
-}