recover-rtti-0.5.1: src/Debug/RecoverRTTI/FlatClosure.hs
module Debug.RecoverRTTI.FlatClosure (
FlatClosure(..)
, getBoxedClosureData
-- * Re-exports
, Box(..)
, asBox
) where
import Control.Exception (evaluate)
import Control.Monad
import GHC.Exts.Heap (Box(..), asBox)
import GHC.Exts.Heap qualified as H
-- | Flattened form of 'Closure' (with indirection nodes removed)
--
-- We only include the fields of 'Closure' that we are interested in.
--
-- TODO: For functions ('FunClosure', 'PAPClosure') we don't currently include
-- any information at all. We could potentially do better here.
data FlatClosure =
-- | Constructor application
ConstrClosure {
ptrArgs :: [Box]
, pkg :: String
, modl :: String
, name :: String
}
-- | Functions
--
-- We map 'H.FunClosure', 'H.PAPClosure' and H.BCOClosure' all to this.
| FunClosure
-- | Unrecognized closure type
| OtherClosure H.Closure
deriving (Show)
getBoxedClosureData :: Box -> IO FlatClosure
getBoxedClosureData b = do
tryForceBox b
fromClosure =<< H.getBoxedClosureData b
where
fromClosure :: H.Closure -> IO FlatClosure
fromClosure = \case
-- Indirections
--
-- For background on black holes, see "Implementing Lazy Functional
-- Languages on Stock Hardware: The Spineless Tagless G-machine", Simon
-- Peyton Jones, Journal of Functional Programming, July 1992, section
-- 9.3.3 "Black holes".
H.BlackholeClosure _ x' -> getBoxedClosureData x'
H.IndClosure _ x' -> getBoxedClosureData x'
-- Constructor application
H.ConstrClosure{ptrArgs, pkg, modl, name} ->
return $ ConstrClosure{..}
-- Functions
H.FunClosure{} -> return $ FunClosure
H.PAPClosure{} -> return $ FunClosure
H.BCOClosure{} -> return $ FunClosure
-- Other kinds of constructors
otherClosure ->
return $ OtherClosure otherClosure
-- | Force the value to WHNF, if possible
--
-- We /cannot/ force the argument until we know what kind of closure we're
-- dealing with. If this is an unlifted closure, forcing it will result in a
-- ghc runtime crash.
tryForceBox :: Box -> IO ()
tryForceBox b@(Box x) = do
closure <- H.getBoxedClosureData b
case closure of
H.APClosure{} -> void $ evaluate x
H.ThunkClosure{} -> void $ evaluate x
H.SelectorClosure{} -> void $ evaluate x
_otherwise -> return ()