1 -----------------------------------------------------------------------------
3 -- Monadery code used in InteractiveUI
5 -- (c) The GHC Team 2005-2006
7 -----------------------------------------------------------------------------
11 #include "HsVersions.h"
14 import Outputable hiding (printForUser)
15 import qualified Outputable
16 import Panic hiding (showException)
24 import Control.Concurrent
25 import Control.Exception as Exception
28 import Data.Int ( Int64 )
34 import Control.Monad as Monad
37 -----------------------------------------------------------------------------
40 data GHCiState = GHCiState
47 session :: GHC.Session,
48 options :: [GHCiOption],
49 prelude :: GHC.Module,
50 resume :: [(SrcSpan, ThreadId, GHC.ResumeHandle)],
51 breaks :: !ActiveBreakPoints,
52 tickarrays :: ModuleEnv TickArray
53 -- tickarrays caches the TickArray for loaded modules,
54 -- so that we don't rebuild it each time the user sets
58 type TickArray = Array Int [(BreakIndex,SrcSpan)]
61 = ShowTiming -- show time/allocs after evaluation
62 | ShowType -- show the type of expressions
63 | RevertCAFs -- revert CAFs after every evaluation
66 data ActiveBreakPoints
68 { breakCounter :: !Int
69 , breakLocations :: ![(Int, BreakLocation)] -- break location uniquely numbered
72 instance Outputable ActiveBreakPoints where
73 ppr activeBrks = prettyLocations $ breakLocations activeBrks
75 emptyActiveBreakPoints :: ActiveBreakPoints
76 emptyActiveBreakPoints
77 = ActiveBreakPoints { breakCounter = 0, breakLocations = [] }
81 { breakModule :: !GHC.Module
82 , breakLoc :: !SrcSpan
83 , breakTick :: {-# UNPACK #-} !Int
87 prettyLocations :: [(Int, BreakLocation)] -> SDoc
88 prettyLocations [] = text "No active breakpoints."
89 prettyLocations locs = vcat $ map (\(i, loc) -> brackets (int i) <+> ppr loc) $ reverse $ locs
91 instance Outputable BreakLocation where
92 ppr loc = (ppr $ breakModule loc) <+> ppr (breakLoc loc)
94 getActiveBreakPoints :: GHCi ActiveBreakPoints
95 getActiveBreakPoints = liftM breaks getGHCiState
97 -- don't reset the counter back to zero?
98 discardActiveBreakPoints :: GHCi ()
99 discardActiveBreakPoints = do
101 let oldActiveBreaks = breaks st
102 newActiveBreaks = oldActiveBreaks { breakLocations = [] }
103 setGHCiState $ st { breaks = newActiveBreaks }
105 deleteBreak :: Int -> GHCi ()
106 deleteBreak identity = do
108 let oldActiveBreaks = breaks st
109 oldLocations = breakLocations oldActiveBreaks
110 newLocations = filter (\loc -> fst loc /= identity) oldLocations
111 newActiveBreaks = oldActiveBreaks { breakLocations = newLocations }
112 setGHCiState $ st { breaks = newActiveBreaks }
114 recordBreak :: BreakLocation -> GHCi (Bool{- was already present -}, Int)
115 recordBreak brkLoc = do
117 let oldActiveBreaks = breaks st
118 let oldLocations = breakLocations oldActiveBreaks
119 -- don't store the same break point twice
120 case [ nm | (nm, loc) <- oldLocations, loc == brkLoc ] of
121 (nm:_) -> return (True, nm)
123 let oldCounter = breakCounter oldActiveBreaks
124 newCounter = oldCounter + 1
127 { breakCounter = newCounter
128 , breakLocations = (oldCounter, brkLoc) : oldLocations
130 setGHCiState $ st { breaks = newActiveBreaks }
131 return (False, oldCounter)
133 newtype GHCi a = GHCi { unGHCi :: IORef GHCiState -> IO a }
135 startGHCi :: GHCi a -> GHCiState -> IO a
136 startGHCi g state = do ref <- newIORef state; unGHCi g ref
138 instance Monad GHCi where
139 (GHCi m) >>= k = GHCi $ \s -> m s >>= \a -> unGHCi (k a) s
140 return a = GHCi $ \s -> return a
142 ghciHandleDyn :: Typeable t => (t -> GHCi a) -> GHCi a -> GHCi a
143 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
144 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
146 getGHCiState = GHCi $ \r -> readIORef r
147 setGHCiState s = GHCi $ \r -> writeIORef r s
149 -- for convenience...
150 getSession = getGHCiState >>= return . session
151 getPrelude = getGHCiState >>= return . prelude
153 GLOBAL_VAR(saved_sess, no_saved_sess, GHC.Session)
154 no_saved_sess = error "no saved_ses"
155 saveSession = getSession >>= io . writeIORef saved_sess
156 splatSavedSession = io (writeIORef saved_sess no_saved_sess)
157 restoreSession = readIORef saved_sess
161 io (GHC.getSessionDynFlags s)
162 setDynFlags dflags = do
164 io (GHC.setSessionDynFlags s dflags)
166 isOptionSet :: GHCiOption -> GHCi Bool
168 = do st <- getGHCiState
169 return (opt `elem` options st)
171 setOption :: GHCiOption -> GHCi ()
173 = do st <- getGHCiState
174 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
176 unsetOption :: GHCiOption -> GHCi ()
178 = do st <- getGHCiState
179 setGHCiState (st{ options = filter (/= opt) (options st) })
182 io m = GHCi { unGHCi = \s -> m >>= return }
184 popResume :: GHCi (Maybe (SrcSpan, ThreadId, GHC.ResumeHandle))
189 (x:xs) -> do setGHCiState $ st { resume = xs } ; return (Just x)
191 pushResume :: SrcSpan -> ThreadId -> GHC.ResumeHandle -> GHCi ()
192 pushResume span threadId resumeAction = do
194 let oldResume = resume st
195 setGHCiState $ st { resume = (span, threadId, resumeAction) : oldResume }
197 discardResumeContext :: GHCi ()
198 discardResumeContext = do
200 setGHCiState st { resume = [] }
202 printForUser :: SDoc -> GHCi ()
203 printForUser doc = do
204 session <- getSession
205 unqual <- io (GHC.getPrintUnqual session)
206 io $ Outputable.printForUser stdout unqual doc
208 -- --------------------------------------------------------------------------
209 -- timing & statistics
211 timeIt :: GHCi a -> GHCi a
213 = do b <- isOptionSet ShowTiming
216 else do allocs1 <- io $ getAllocations
217 time1 <- io $ getCPUTime
219 allocs2 <- io $ getAllocations
220 time2 <- io $ getCPUTime
221 io $ printTimes (fromIntegral (allocs2 - allocs1))
225 foreign import ccall unsafe "getAllocations" getAllocations :: IO Int64
226 -- defined in ghc/rts/Stats.c
228 printTimes :: Integer -> Integer -> IO ()
229 printTimes allocs psecs
230 = do let secs = (fromIntegral psecs / (10^12)) :: Float
231 secs_str = showFFloat (Just 2) secs
233 parens (text (secs_str "") <+> text "secs" <> comma <+>
234 text (show allocs) <+> text "bytes")))
236 -----------------------------------------------------------------------------
243 -- Have to turn off buffering again, because we just
244 -- reverted stdout, stderr & stdin to their defaults.
246 foreign import ccall "revertCAFs" rts_revertCAFs :: IO ()
247 -- Make it "safe", just in case
249 -----------------------------------------------------------------------------
250 -- To flush buffers for the *interpreted* computation we need
251 -- to refer to *its* stdout/stderr handles
253 GLOBAL_VAR(flush_interp, error "no flush_interp", IO ())
254 GLOBAL_VAR(turn_off_buffering, error "no flush_stdout", IO ())
256 command_sequence :: [String] -> String
257 command_sequence = unwords . intersperse "Prelude.>>"
259 no_buffer :: String -> String
260 no_buffer h = unwords ["System.IO.hSetBuffering",
262 "System.IO.NoBuffering"]
265 no_buf_cmd = command_sequence $ map no_buffer ["stdout", "stderr", "stdin"]
267 flush_buffer :: String -> String
268 flush_buffer h = unwords ["System.IO.hFlush", "System.IO." ++ h]
271 flush_cmd = command_sequence [flush_buffer "stdout", flush_buffer "stderr"]
273 initInterpBuffering :: GHC.Session -> IO ()
274 initInterpBuffering session
275 = do -- we don't want to be fooled by any modules lying around in the current
276 -- directory when we compile these code fragments, so set the import
277 -- path to be empty while we compile them.
278 dflags <- GHC.getSessionDynFlags session
279 GHC.setSessionDynFlags session dflags{importPaths=[]}
281 maybe_hval <- GHC.compileExpr session no_buf_cmd
284 Just hval -> writeIORef turn_off_buffering (unsafeCoerce# hval :: IO ())
285 other -> panic "interactiveUI:setBuffering"
287 maybe_hval <- GHC.compileExpr session flush_cmd
289 Just hval -> writeIORef flush_interp (unsafeCoerce# hval :: IO ())
290 _ -> panic "interactiveUI:flush"
292 GHC.setSessionDynFlags session dflags
293 GHC.workingDirectoryChanged session
297 flushInterpBuffers :: GHCi ()
299 = io $ do Monad.join (readIORef flush_interp)
302 turnOffBuffering :: IO ()
304 = do Monad.join (readIORef turn_off_buffering)