Rodrigo Mesquita pushed to branch wip/romes/exec-break-has-resume at Glasgow Haskell Compiler / GHC
Commits:
-
1957d4e6
by Rodrigo Mesquita at 2026-10-29T11:54:26+00:00
6 changed files:
- compiler/GHC.hs
- compiler/GHC/Runtime/Context.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval.hs
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Monad.hs
Changes:
| ... | ... | @@ -203,8 +203,7 @@ module GHC ( |
| 203 | 203 | Resume(..),
|
| 204 | 204 | History(historyBreakpointId, historyEnclosingDecls),
|
| 205 | 205 | GHC.getHistorySpan, getHistoryModule,
|
| 206 | - abandon, abandonAll,
|
|
| 207 | - getResumeContext,
|
|
| 206 | + abandon,
|
|
| 208 | 207 | GHC.obtainTermFromId, GHC.obtainTermFromVal, reconstructType,
|
| 209 | 208 | modInfoModBreaks,
|
| 210 | 209 | ModBreaks(..), BreakTickIndex,
|
| ... | ... | @@ -24,7 +24,7 @@ import GHC.Hs |
| 24 | 24 | import GHC.Driver.DynFlags
|
| 25 | 25 | import {-# SOURCE #-} GHC.Driver.Plugins
|
| 26 | 26 | |
| 27 | -import GHC.Runtime.Eval.Types ( IcGlobalRdrEnv(..), Resume )
|
|
| 27 | +import GHC.Runtime.Eval.Types ( IcGlobalRdrEnv(..) )
|
|
| 28 | 28 | |
| 29 | 29 | import GHC.Unit
|
| 30 | 30 | import GHC.Unit.Env
|
| ... | ... | @@ -322,9 +322,6 @@ data InteractiveContext |
| 322 | 322 | ic_default :: DefaultEnv,
|
| 323 | 323 | -- ^ The current default classes and types, set by 'default' declarations
|
| 324 | 324 | |
| 325 | - ic_resume :: [Resume],
|
|
| 326 | - -- ^ The stack of breakpoint contexts
|
|
| 327 | - |
|
| 328 | 325 | ic_monad :: ExactRdrName,
|
| 329 | 326 | -- ^ The monad that GHCi is executing in
|
| 330 | 327 | |
| ... | ... | @@ -370,7 +367,6 @@ emptyInteractiveContext dflags |
| 370 | 367 | ic_monad = ExactOcc ioTyConOcc, -- IO monad by default
|
| 371 | 368 | ic_int_print = ExactOcc printIdOcc, -- System.IO.print by default
|
| 372 | 369 | ic_default = emptyDefaultEnv,
|
| 373 | - ic_resume = [],
|
|
| 374 | 370 | ic_cwd = Nothing,
|
| 375 | 371 | ic_plugins = emptyPlugins
|
| 376 | 372 | }
|
| ... | ... | @@ -266,32 +266,21 @@ mkBreakpointOccurrences = do |
| 266 | 266 | -- Getting current breakpoint information
|
| 267 | 267 | --------------------------------------------------------------------------------
|
| 268 | 268 | |
| 269 | -getCurrentBreakSpan :: GhcMonad m => m (Maybe SrcSpan)
|
|
| 270 | -getCurrentBreakSpan = do
|
|
| 269 | +getResumeBreakSpan :: GhcMonad m => Resume -> m SrcSpan
|
|
| 270 | +getResumeBreakSpan r = do
|
|
| 271 | 271 | hug <- hsc_HUG <$> getSession
|
| 272 | - resumes <- getResumeContext
|
|
| 273 | - case resumes of
|
|
| 274 | - [] -> return Nothing
|
|
| 275 | - (r:_) -> do
|
|
| 276 | - let ix = resumeHistoryIx r
|
|
| 277 | - if ix == 0
|
|
| 278 | - then return (Just (resumeSpan r))
|
|
| 279 | - else do
|
|
| 280 | - let hist = resumeHistory r !! (ix-1)
|
|
| 281 | - pan <- liftIO $ getHistorySpan hug hist
|
|
| 282 | - return (Just pan)
|
|
| 272 | + case resumeHistoryIx r of
|
|
| 273 | + 0 -> return (resumeSpan r)
|
|
| 274 | + ix -> liftIO $ getHistorySpan hug (resumeHistory r !! (ix-1))
|
|
| 283 | 275 | |
| 284 | -getCurrentBreakModule :: GhcMonad m => m (Maybe Module)
|
|
| 285 | -getCurrentBreakModule = do
|
|
| 286 | - resumes <- getResumeContext
|
|
| 276 | +getResumeBreakModule :: GhcMonad m => Resume -> m (Maybe Module)
|
|
| 277 | +getResumeBreakModule r = do
|
|
| 287 | 278 | hug <- hsc_HUG <$> getSession
|
| 288 | - liftIO $ case resumes of
|
|
| 289 | - [] -> pure Nothing
|
|
| 290 | - (r:_) -> case resumeHistoryIx r of
|
|
| 291 | - 0 -> case resumeBreakpointId r of
|
|
| 292 | - Nothing -> pure Nothing
|
|
| 293 | - Just ibi -> do
|
|
| 294 | - brks <- readIModBreaks hug ibi
|
|
| 295 | - return $ Just $ getBreakSourceMod ibi brks
|
|
| 296 | - ix ->
|
|
| 297 | - Just <$> getHistoryModule hug (resumeHistory r !! (ix-1)) |
|
| 279 | + liftIO $ case resumeHistoryIx r of
|
|
| 280 | + 0 -> case resumeBreakpointId r of
|
|
| 281 | + Nothing -> pure Nothing
|
|
| 282 | + Just ibi -> do
|
|
| 283 | + brks <- readIModBreaks hug ibi
|
|
| 284 | + return $ Just $ getBreakSourceMod ibi brks
|
|
| 285 | + ix ->
|
|
| 286 | + Just <$> getHistoryModule hug (resumeHistory r !! (ix-1)) |
| ... | ... | @@ -14,8 +14,7 @@ module GHC.Runtime.Eval ( |
| 14 | 14 | execStmt, execStmt', ExecOptions(..), execOptions, ExecResult(..), resumeExec,
|
| 15 | 15 | runDecls, runDeclsWithLocation, runParsedDecls,
|
| 16 | 16 | parseImportDecl, SingleStep(..),
|
| 17 | - abandon, abandonAll,
|
|
| 18 | - getResumeContext,
|
|
| 17 | + abandon,
|
|
| 19 | 18 | getHistorySpan,
|
| 20 | 19 | getModBreaks, readIModBreaks, readIModModBreaks,
|
| 21 | 20 | getHistoryModule,
|
| ... | ... | @@ -140,9 +139,6 @@ import GHCi.BreakArray (BreakArray) |
| 140 | 139 | -- -----------------------------------------------------------------------------
|
| 141 | 140 | -- running a statement interactively
|
| 142 | 141 | |
| 143 | -getResumeContext :: GhcMonad m => m [Resume]
|
|
| 144 | -getResumeContext = withSession (return . ic_resume . hsc_IC)
|
|
| 145 | - |
|
| 146 | 142 | mkHistory :: HUG.HomeUnitGraph -> ForeignHValue -> InternalBreakpointId -> IO History
|
| 147 | 143 | mkHistory hug hval ibi = History hval ibi <$> findEnclosingDecls hug ibi
|
| 148 | 144 | |
| ... | ... | @@ -341,9 +337,8 @@ handleRunStatus step expr bindings final_ids status history0 = do |
| 341 | 337 | , resumeCCS = ccs
|
| 342 | 338 | , resumeHistoryIx = 0
|
| 343 | 339 | }
|
| 344 | - hsc_env2 = pushResume hsc_env1 resume
|
|
| 345 | 340 | |
| 346 | - setSession hsc_env2
|
|
| 341 | + setSession hsc_env1
|
|
| 347 | 342 | return (ExecBreak break_ids Nothing resume)
|
| 348 | 343 | |
| 349 | 344 | -- EvalBreak (Just ...) case: the interpreter stopped at a breakpoint
|
| ... | ... | @@ -388,8 +383,7 @@ handleRunStatus step expr bindings final_ids status history0 = do |
| 388 | 383 | , resumeCCS = ccs
|
| 389 | 384 | , resumeHistoryIx = 0
|
| 390 | 385 | }
|
| 391 | - hsc_env2 = pushResume hsc_env1 resume
|
|
| 392 | - setSession hsc_env2
|
|
| 386 | + setSession hsc_env1
|
|
| 393 | 387 | return (ExecBreak break_ids (Just ibi) resume)
|
| 394 | 388 | else do
|
| 395 | 389 | -- resume with the same step type
|
| ... | ... | @@ -409,10 +403,6 @@ handleRunStatus step expr bindings final_ids status history0 = do |
| 409 | 403 | --
|
| 410 | 404 | -- The interactive things in scope are restored to the things available at the
|
| 411 | 405 | -- time this paused thread hit the breakpoint.
|
| 412 | ---
|
|
| 413 | --- This 'Resume' is not popped off the interactive `ic_resume` breakpoint
|
|
| 414 | --- stack, since there's no way to tell which of the breakpoints in the stack is
|
|
| 415 | --- the one we're resuming.
|
|
| 416 | 406 | resumeExec :: GhcMonad m
|
| 417 | 407 | => SingleStep
|
| 418 | 408 | -> Maybe Int
|
| ... | ... | @@ -495,58 +485,49 @@ getBreakArray interp InternalBreakpointId{ibi_info_mod} imbs = do |
| 495 | 485 | ( ld_st'
|
| 496 | 486 | , ba
|
| 497 | 487 | )
|
| 498 | -back :: GhcMonad m => Int -> m ([Id], Int, SrcSpan)
|
|
| 488 | + |
|
| 489 | +back :: GhcMonad m => Int -> Resume -> m (Resume, [Id], SrcSpan)
|
|
| 499 | 490 | back n = moveHist (+n)
|
| 500 | 491 | |
| 501 | -forward :: GhcMonad m => Int -> m ([Id], Int, SrcSpan)
|
|
| 492 | +forward :: GhcMonad m => Int -> Resume -> m (Resume, [Id], SrcSpan)
|
|
| 502 | 493 | forward n = moveHist (subtract n)
|
| 503 | 494 | |
| 504 | -moveHist :: GhcMonad m => (Int -> Int) -> m ([Id], Int, SrcSpan)
|
|
| 505 | -moveHist fn = do
|
|
| 495 | +moveHist :: GhcMonad m => (Int -> Int) -> Resume -> m (Resume, [Id], SrcSpan)
|
|
| 496 | +moveHist fn r = do
|
|
| 506 | 497 | hsc_env <- getSession
|
| 507 | - case ic_resume (hsc_IC hsc_env) of
|
|
| 508 | - [] -> liftIO $
|
|
| 509 | - throwGhcExceptionIO (ProgramError "not stopped at a breakpoint")
|
|
| 510 | - (r:rs) -> do
|
|
| 511 | - let ix = resumeHistoryIx r
|
|
| 512 | - history = resumeHistory r
|
|
| 513 | - new_ix = fn ix
|
|
| 514 | - --
|
|
| 515 | - when (history `lengthLessThan` new_ix) $ liftIO $
|
|
| 516 | - throwGhcExceptionIO (ProgramError "no more logged breakpoints")
|
|
| 517 | - when (new_ix < 0) $ liftIO $
|
|
| 518 | - throwGhcExceptionIO (ProgramError "already at the beginning of the history")
|
|
| 519 | - |
|
| 520 | - let
|
|
| 521 | - update_ic apStack mb_info = do
|
|
| 522 | - span <- case mb_info of
|
|
| 523 | - Nothing -> return $ mkGeneralSrcSpan (fsLit "<unknown>")
|
|
| 524 | - Just ibi -> liftIO $ do
|
|
| 525 | - let hug = hsc_HUG hsc_env
|
|
| 526 | - brks <- readIModBreaks hug ibi
|
|
| 527 | - getBreakLoc (readIModModBreaks hug) ibi brks
|
|
| 528 | - (hsc_env1, final_ids) <-
|
|
| 529 | - liftIO $ bindLocalsAtBreakpoint hsc_env apStack span mb_info
|
|
| 530 | - let ic = hsc_IC hsc_env1
|
|
| 531 | - r' = r { resumeHistoryIx = new_ix }
|
|
| 532 | - ic' = ic { ic_resume = r':rs }
|
|
| 533 | - |
|
| 534 | - setSession hsc_env1{ hsc_IC = ic' }
|
|
| 535 | - |
|
| 536 | - return (final_ids, new_ix, span)
|
|
| 537 | - |
|
| 538 | - -- careful: we want apStack to be the AP_STACK itself, not a thunk
|
|
| 539 | - -- around it, hence the cases are carefully constructed below to
|
|
| 540 | - -- make this the case. ToDo: this is v. fragile, do something better.
|
|
| 541 | - if new_ix == 0
|
|
| 542 | - then case r of
|
|
| 543 | - Resume { resumeApStack = apStack,
|
|
| 544 | - resumeBreakpointId = mb_brkpt } ->
|
|
| 545 | - update_ic apStack mb_brkpt
|
|
| 546 | - else case history !! (new_ix - 1) of
|
|
| 547 | - History{..} ->
|
|
| 548 | - update_ic historyApStack (Just historyBreakpointId)
|
|
| 498 | + let ix = resumeHistoryIx r
|
|
| 499 | + history = resumeHistory r
|
|
| 500 | + new_ix = fn ix
|
|
| 501 | + --
|
|
| 502 | + when (history `lengthLessThan` new_ix) $ liftIO $
|
|
| 503 | + throwGhcExceptionIO (ProgramError "no more logged breakpoints")
|
|
| 504 | + when (new_ix < 0) $ liftIO $
|
|
| 505 | + throwGhcExceptionIO (ProgramError "already at the beginning of the history")
|
|
| 549 | 506 | |
| 507 | + let
|
|
| 508 | + update_ic apStack mb_info = do
|
|
| 509 | + span <- case mb_info of
|
|
| 510 | + Nothing -> return $ mkGeneralSrcSpan (fsLit "<unknown>")
|
|
| 511 | + Just ibi -> liftIO $ do
|
|
| 512 | + let hug = hsc_HUG hsc_env
|
|
| 513 | + brks <- readIModBreaks hug ibi
|
|
| 514 | + getBreakLoc (readIModModBreaks hug) ibi brks
|
|
| 515 | + (hsc_env1, final_ids) <-
|
|
| 516 | + liftIO $ bindLocalsAtBreakpoint hsc_env apStack span mb_info
|
|
| 517 | + setSession hsc_env1
|
|
| 518 | + return (r { resumeHistoryIx = new_ix }, final_ids, span)
|
|
| 519 | + |
|
| 520 | + -- careful: we want apStack to be the AP_STACK itself, not a thunk
|
|
| 521 | + -- around it, hence the cases are carefully constructed below to
|
|
| 522 | + -- make this the case. ToDo: this is v. fragile, do something better.
|
|
| 523 | + if new_ix == 0
|
|
| 524 | + then case r of
|
|
| 525 | + Resume { resumeApStack = apStack,
|
|
| 526 | + resumeBreakpointId = mb_brkpt } ->
|
|
| 527 | + update_ic apStack mb_brkpt
|
|
| 528 | + else case history !! (new_ix - 1) of
|
|
| 529 | + History{..} ->
|
|
| 530 | + update_ic historyApStack (Just historyBreakpointId)
|
|
| 550 | 531 | |
| 551 | 532 | -- -----------------------------------------------------------------------------
|
| 552 | 533 | -- After stopping at a breakpoint, add free variables to the environment
|
| ... | ... | @@ -718,12 +699,6 @@ rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do |
| 718 | 699 | let ic' = substInteractiveContext ic subst
|
| 719 | 700 | return (hsc_env{hsc_IC=ic'}, subst `composeTCvSubst` last_subst)
|
| 720 | 701 | |
| 721 | -pushResume :: HscEnv -> Resume -> HscEnv
|
|
| 722 | -pushResume hsc_env resume = hsc_env { hsc_IC = ictxt1 }
|
|
| 723 | - where
|
|
| 724 | - ictxt0 = hsc_IC hsc_env
|
|
| 725 | - ictxt1 = ictxt0 { ic_resume = resume : ic_resume ictxt0 }
|
|
| 726 | - |
|
| 727 | 702 | |
| 728 | 703 | {-
|
| 729 | 704 | Note [Syncing breakpoint info]
|
| ... | ... | @@ -756,31 +731,10 @@ pushResume hsc_env resume = hsc_env { hsc_IC = ictxt1 } |
| 756 | 731 | -- -----------------------------------------------------------------------------
|
| 757 | 732 | -- Abandoning a resume context
|
| 758 | 733 | |
| 759 | -abandon :: GhcMonad m => m Bool
|
|
| 760 | -abandon = do
|
|
| 761 | - hsc_env <- getSession
|
|
| 762 | - let ic = hsc_IC hsc_env
|
|
| 763 | - resume = ic_resume ic
|
|
| 764 | - interp = hscInterp hsc_env
|
|
| 765 | - case resume of
|
|
| 766 | - [] -> return False
|
|
| 767 | - r:rs -> do
|
|
| 768 | - setSession hsc_env{ hsc_IC = ic { ic_resume = rs } }
|
|
| 769 | - liftIO $ abandonStmt interp (resumeContext r)
|
|
| 770 | - return True
|
|
| 771 | - |
|
| 772 | -abandonAll :: GhcMonad m => m Bool
|
|
| 773 | -abandonAll = do
|
|
| 774 | - hsc_env <- getSession
|
|
| 775 | - let ic = hsc_IC hsc_env
|
|
| 776 | - resume = ic_resume ic
|
|
| 777 | - interp = hscInterp hsc_env
|
|
| 778 | - case resume of
|
|
| 779 | - [] -> return False
|
|
| 780 | - rs -> do
|
|
| 781 | - setSession hsc_env{ hsc_IC = ic { ic_resume = [] } }
|
|
| 782 | - liftIO $ mapM_ (abandonStmt interp. resumeContext) rs
|
|
| 783 | - return True
|
|
| 734 | +abandon :: GhcMonad m => Resume -> m ()
|
|
| 735 | +abandon r = do
|
|
| 736 | + interp <- hscInterp <$> getSession
|
|
| 737 | + liftIO $ abandonStmt interp (resumeContext r)
|
|
| 784 | 738 | |
| 785 | 739 | -- -----------------------------------------------------------------------------
|
| 786 | 740 | -- Bounded list, optimised for repeated cons
|
| ... | ... | @@ -558,6 +558,7 @@ interactiveUI config baseDFlags srcs maybe_exprs = do |
| 558 | 558 | line_number = 0,
|
| 559 | 559 | break_ctr = 0,
|
| 560 | 560 | breaks = IntMap.empty,
|
| 561 | + resume_stack = [],
|
|
| 561 | 562 | tickarrays = emptyModuleEnv,
|
| 562 | 563 | internalBreaks = emptyModuleEnv,
|
| 563 | 564 | ghci_commands = availableCommands config,
|
| ... | ... | @@ -1210,7 +1211,7 @@ getInfoForPrompt :: GhciMonad m => m (SDoc, [String], Int) |
| 1210 | 1211 | getInfoForPrompt = do
|
| 1211 | 1212 | st <- getGHCiState
|
| 1212 | 1213 | imports <- GHC.getContext
|
| 1213 | - resumes <- GHC.getResumeContext
|
|
| 1214 | + resumes <- getResumeContext
|
|
| 1214 | 1215 | |
| 1215 | 1216 | context_bit <-
|
| 1216 | 1217 | case resumes of
|
| ... | ... | @@ -1831,9 +1832,9 @@ isPrefixOptOf :: String -> String -> Bool |
| 1831 | 1832 | isPrefixOptOf s x = let (body, opt) = break (== '!') s
|
| 1832 | 1833 | in (body `isPrefixOf` x) && (opt `isSuffixOf` x)
|
| 1833 | 1834 | |
| 1834 | -getCallStackAtCurrentBreakpoint :: GHC.GhcMonad m => m (Maybe [String])
|
|
| 1835 | +getCallStackAtCurrentBreakpoint :: GhciMonad m => m (Maybe [String])
|
|
| 1835 | 1836 | getCallStackAtCurrentBreakpoint = do
|
| 1836 | - resumes <- GHC.getResumeContext
|
|
| 1837 | + resumes <- getResumeContext
|
|
| 1837 | 1838 | case resumes of
|
| 1838 | 1839 | [] -> return Nothing
|
| 1839 | 1840 | (r:_) -> do
|
| ... | ... | @@ -2355,7 +2356,7 @@ loadModule' loadTargets files = do |
| 2355 | 2356 | |
| 2356 | 2357 | let load_module = do
|
| 2357 | 2358 | -- unload first
|
| 2358 | - _ <- GHC.abandonAll
|
|
| 2359 | + abandonAll
|
|
| 2359 | 2360 | clearCaches
|
| 2360 | 2361 | |
| 2361 | 2362 | GHC.setTargets targets
|
| ... | ... | @@ -3931,9 +3932,9 @@ showBkptTable = do |
| 3931 | 3932 | st <- getGHCiState
|
| 3932 | 3933 | printForUser $ prettyLocations (breaks st)
|
| 3933 | 3934 | |
| 3934 | -showContext :: GHC.GhcMonad m => m ()
|
|
| 3935 | +showContext :: GhciMonad m => m ()
|
|
| 3935 | 3936 | showContext = do
|
| 3936 | - resumes <- GHC.getResumeContext
|
|
| 3937 | + resumes <- getResumeContext
|
|
| 3937 | 3938 | docs <- mapM pp_resume (reverse resumes)
|
| 3938 | 3939 | printForUser $ vcat docs
|
| 3939 | 3940 | where
|
| ... | ... | @@ -4400,7 +4401,7 @@ doContinue' step mbCnt= do |
| 4400 | 4401 | |
| 4401 | 4402 | abandonCmd :: GhciMonad m => String -> m ()
|
| 4402 | 4403 | abandonCmd = noArgs $ withSandboxOnly ":abandon" $ do
|
| 4403 | - b <- GHC.abandon -- the prompt will change to indicate the new context
|
|
| 4404 | + b <- abandon -- the prompt will change to indicate the new context
|
|
| 4404 | 4405 | when (not b) $ liftIO $ putStrLn "There is no computation running."
|
| 4405 | 4406 | |
| 4406 | 4407 | deleteCmd :: GhciMonad m => String -> m ()
|
| ... | ... | @@ -4477,14 +4478,14 @@ enaDisaAllBreaks enaDisa = do |
| 4477 | 4478 | st <- getGHCiState
|
| 4478 | 4479 | mapM_ (enaDisaAssoc enaDisa) $ IntMap.assocs $ breaks st
|
| 4479 | 4480 | |
| 4480 | -historyCmd :: GHC.GhcMonad m => String -> m ()
|
|
| 4481 | +historyCmd :: GhciMonad m => String -> m ()
|
|
| 4481 | 4482 | historyCmd arg
|
| 4482 | 4483 | | null arg = history 20
|
| 4483 | 4484 | | all isDigit arg = history (read arg)
|
| 4484 | 4485 | | otherwise = liftIO $ putStrLn "Syntax: :history [num]"
|
| 4485 | 4486 | where
|
| 4486 | 4487 | history num = do
|
| 4487 | - resumes <- GHC.getResumeContext
|
|
| 4488 | + resumes <- getResumeContext
|
|
| 4488 | 4489 | case resumes of
|
| 4489 | 4490 | [] -> liftIO $ putStrLn "Not stopped at a breakpoint"
|
| 4490 | 4491 | (r:_) -> do
|
| ... | ... | @@ -4546,7 +4547,7 @@ backCmd arg |
| 4546 | 4547 | | otherwise = liftIO $ putStrLn "Syntax: :back [num]"
|
| 4547 | 4548 | where
|
| 4548 | 4549 | back num = withSandboxOnly ":back" $ do
|
| 4549 | - (ids, _, pan) <- GHC.back num
|
|
| 4550 | + (ids, _, pan) <- moveHist (GHC.back num)
|
|
| 4550 | 4551 | printForUser $ text "Logged breakpoint at" <+> ppr pan
|
| 4551 | 4552 | printTypeOfIds ids
|
| 4552 | 4553 | -- run the command set with ":set stop <cmd>"
|
| ... | ... | @@ -4560,7 +4561,7 @@ forwardCmd arg |
| 4560 | 4561 | | otherwise = liftIO $ putStrLn "Syntax: :forward [num]"
|
| 4561 | 4562 | where
|
| 4562 | 4563 | forward num = withSandboxOnly ":forward" $ do
|
| 4563 | - (ids, ix, pan) <- GHC.forward num
|
|
| 4564 | + (ids, ix, pan) <- moveHist (GHC.forward num)
|
|
| 4564 | 4565 | printForUser $ (if (ix == 0)
|
| 4565 | 4566 | then text "Stopped at"
|
| 4566 | 4567 | else text "Logged breakpoint at") <+> ppr pan
|
| ... | ... | @@ -4677,7 +4678,7 @@ The names of nested functions are stored in `ModBreaks.modBreaks_decls`. |
| 4677 | 4678 | -----------------------------------------------------------------------------
|
| 4678 | 4679 | -- :where
|
| 4679 | 4680 | |
| 4680 | -whereCmd :: GHC.GhcMonad m => String -> m ()
|
|
| 4681 | +whereCmd :: GhciMonad m => String -> m ()
|
|
| 4681 | 4682 | whereCmd = noArgs $ do
|
| 4682 | 4683 | mstrs <- getCallStackAtCurrentBreakpoint
|
| 4683 | 4684 | case mstrs of
|
| ... | ... | @@ -4696,7 +4697,7 @@ listCmd "" = do |
| 4696 | 4697 | Just (RealSrcSpan pan _) ->
|
| 4697 | 4698 | listAround pan True
|
| 4698 | 4699 | Just pan@_ ->
|
| 4699 | - do resumes <- GHC.getResumeContext
|
|
| 4700 | + do resumes <- getResumeContext
|
|
| 4700 | 4701 | case resumes of
|
| 4701 | 4702 | [] -> panic "No resumes"
|
| 4702 | 4703 | (r:_) ->
|
| ... | ... | @@ -22,6 +22,8 @@ module GHCi.UI.Monad ( |
| 22 | 22 | extractDynFlags, getDynFlags,
|
| 23 | 23 | |
| 24 | 24 | runStmt, runDecls, runDecls', resume, recordBreak, revertCAFs,
|
| 25 | + getResumeContext, abandon, abandonAll, moveHist,
|
|
| 26 | + getCurrentBreakSpan, getCurrentBreakModule,
|
|
| 25 | 27 | ActionStats(..), runAndPrintStats, runWithStats, printStats,
|
| 26 | 28 | |
| 27 | 29 | prettyLocations,
|
| ... | ... | @@ -97,6 +99,9 @@ data GHCiState = GHCiState |
| 97 | 99 | line_number :: !Int, -- ^ input line
|
| 98 | 100 | break_ctr :: !Int,
|
| 99 | 101 | breaks :: !(IntMap.IntMap BreakLocation),
|
| 102 | + resume_stack :: [GHC.Resume],
|
|
| 103 | + -- ^ The stack of 'Resume's for nested breakpoints hit (hitting a
|
|
| 104 | + -- breakpoint while evaluating an expression at another breakpoint)
|
|
| 100 | 105 | tickarrays :: ModuleEnv TickArray,
|
| 101 | 106 | -- ^ 'tickarrays' caches the 'TickArray' for loaded modules,
|
| 102 | 107 | -- so that we don't rebuild it each time the user sets
|
| ... | ... | @@ -380,7 +385,9 @@ runStmt stmt stmt_text step = do |
| 380 | 385 | , GHC.execSingleStep = step
|
| 381 | 386 | , GHC.execWrap = \fhv -> EvalApp (EvalThis (evalWrapper st))
|
| 382 | 387 | (EvalThis fhv) }
|
| 383 | - Just <$> GHC.execStmt' stmt stmt_text opts
|
|
| 388 | + r <- GHC.execStmt' stmt stmt_text opts
|
|
| 389 | + pushResume r
|
|
| 390 | + return (Just r)
|
|
| 384 | 391 | |
| 385 | 392 | runDecls :: GhciMonad m => String -> m (Maybe [GHC.TyThing])
|
| 386 | 393 | runDecls decls = do
|
| ... | ... | @@ -413,12 +420,70 @@ resume step mbIgnoreCnt = do |
| 413 | 420 | withProgName (progname st) $
|
| 414 | 421 | withArgs (args st) $
|
| 415 | 422 | reflectGHCi x $ do
|
| 416 | - GHC.getResumeContext >>= \case
|
|
| 417 | - [] -> liftIO $
|
|
| 418 | - throwGhcExceptionIO (GHC.ProgramError "not stopped at a breakpoint")
|
|
| 419 | - (r:rs) -> do
|
|
| 420 | - modifySession $ \env -> env{hsc_IC = (hsc_IC env){ ic_resume = rs }}
|
|
| 421 | - GHC.resumeExec step mbIgnoreCnt r
|
|
| 423 | + r <- popResume >>= \case
|
|
| 424 | + Nothing -> liftIO $
|
|
| 425 | + throwGhcExceptionIO (GHC.ProgramError "not stopped at a breakpoint")
|
|
| 426 | + Just r -> return r
|
|
| 427 | + res <- GHC.resumeExec step mbIgnoreCnt r
|
|
| 428 | + pushResume res
|
|
| 429 | + return res
|
|
| 430 | + |
|
| 431 | +-- -----------------------------------------------------------------------------
|
|
| 432 | +-- Resume helpers
|
|
| 433 | + |
|
| 434 | +pushResume :: GhciMonad m => GHC.ExecResult -> m ()
|
|
| 435 | +pushResume GHC.ExecBreak{GHC.breakResume = r} =
|
|
| 436 | + modifyGHCiState $ \st -> st{ resume_stack = r : resume_stack st }
|
|
| 437 | +pushResume _ = return ()
|
|
| 438 | + |
|
| 439 | +popResume :: GhciMonad m => m (Maybe GHC.Resume)
|
|
| 440 | +popResume = do
|
|
| 441 | + st <- getGHCiState
|
|
| 442 | + case resume_stack st of
|
|
| 443 | + [] -> return Nothing
|
|
| 444 | + r:rs -> do setGHCiState st{ resume_stack = rs }
|
|
| 445 | + return (Just r)
|
|
| 446 | + |
|
| 447 | +abandon :: GhciMonad m => m Bool
|
|
| 448 | +abandon = popResume >>= \case
|
|
| 449 | + Nothing -> return False
|
|
| 450 | + Just r -> GHC.abandon r >> return True
|
|
| 451 | + |
|
| 452 | +abandonAll :: GhciMonad m => m ()
|
|
| 453 | +abandonAll = do
|
|
| 454 | + st <- getGHCiState
|
|
| 455 | + setGHCiState st{ resume_stack = [] }
|
|
| 456 | + mapM_ GHC.abandon (resume_stack st)
|
|
| 457 | + |
|
| 458 | +moveHist :: GhciMonad m
|
|
| 459 | + => (GHC.Resume -> m (GHC.Resume, [GHC.Id], SrcSpan))
|
|
| 460 | + -> m ([GHC.Id], Int, SrcSpan)
|
|
| 461 | +moveHist move = do
|
|
| 462 | + st <- getGHCiState
|
|
| 463 | + case resume_stack st of
|
|
| 464 | + [] -> liftIO $
|
|
| 465 | + throwGhcExceptionIO (GHC.ProgramError "not stopped at a breakpoint")
|
|
| 466 | + r:rs -> do
|
|
| 467 | + (r', ids, pan) <- move r
|
|
| 468 | + setGHCiState st{ resume_stack = r' : rs }
|
|
| 469 | + return (ids, GHC.resumeHistoryIx r', pan)
|
|
| 470 | + |
|
| 471 | +getResumeContext :: GhciMonad m => m [GHC.Resume]
|
|
| 472 | +getResumeContext = resume_stack <$> getGHCiState
|
|
| 473 | + |
|
| 474 | +getCurrentBreakSpan :: GhciMonad m => m (Maybe SrcSpan)
|
|
| 475 | +getCurrentBreakSpan = do
|
|
| 476 | + resumes <- getResumeContext
|
|
| 477 | + case resumes of
|
|
| 478 | + [] -> return Nothing
|
|
| 479 | + r:_ -> Just <$> getResumeBreakSpan r
|
|
| 480 | + |
|
| 481 | +getCurrentBreakModule :: GhciMonad m => m (Maybe Module)
|
|
| 482 | +getCurrentBreakModule = do
|
|
| 483 | + resumes <- getResumeContext
|
|
| 484 | + case resumes of
|
|
| 485 | + [] -> return Nothing
|
|
| 486 | + r:_ -> getResumeBreakModule r
|
|
| 422 | 487 | |
| 423 | 488 | -- --------------------------------------------------------------------------
|
| 424 | 489 | -- timing & statistics
|