Skip to content

Commit bd4481d

Browse files
committed
Make evaluation utilities available in Debugger.Runtime.Eval
These utilities are useful outside of parsing only, and come in quite handy for a lot of multi-threaded and callstack related code.
1 parent 261d7e0 commit bd4481d

File tree

3 files changed

+70
-34
lines changed

3 files changed

+70
-34
lines changed

haskell-debugger.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ library
6464
GHC.Debugger.Stopped.Variables,
6565

6666
GHC.Debugger.Runtime,
67+
GHC.Debugger.Runtime.Eval,
6768
GHC.Debugger.Runtime.Instances,
6869
GHC.Debugger.Runtime.Instances.Discover,
6970

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
-- | Lower-level interface to evaluating things in the (possibly remote) debuggee process
2+
module GHC.Debugger.Runtime.Eval where
3+
4+
import GHC
5+
import GHC.Driver.Env
6+
import GHC.Runtime.Interpreter as Interp
7+
import Control.Monad.Reader
8+
import GHC.Driver.Config
9+
import GHCi.RemoteTypes
10+
11+
import GHC.Debugger.Monad
12+
13+
--------------------------------------------------------------------------------
14+
-- * Evaluation on Foreign Heap Values
15+
--------------------------------------------------------------------------------
16+
17+
-- | Evaluate `f x` for any @f :: a -> b@ and any @x :: a@.
18+
-- The result is the foreign reference to a heap value of type @b@
19+
evalApplication :: ForeignHValue -> ForeignHValue -> Debugger ForeignHValue
20+
evalApplication fref aref = do
21+
hsc_env <- getSession
22+
mk_list_fv <- compileExprRemote "(pure @IO . (:[])) :: a -> IO [a]"
23+
24+
let eval_opts = initEvalOpts (hsc_dflags hsc_env) EvalStepNone
25+
interp = hscInterp hsc_env
26+
27+
liftIO (evalStmt interp eval_opts $ (EvalThis mk_list_fv) `EvalApp` ((EvalThis fref) `EvalApp` (EvalThis aref)))
28+
>>= liftIO . handleSingStatus
29+
30+
-- | Evaluate `f x` for any @f :: a -> IO b@ and any @x :: a@.
31+
-- The result is the foreign reference to a heap value of type @b@ (the IO action is executed)
32+
evalApplicationIO :: ForeignHValue -> ForeignHValue -> Debugger ForeignHValue
33+
evalApplicationIO fref aref = do
34+
hsc_env <- getSession
35+
fmap_list_fv <- compileExprRemote "(fmap (:[])) :: IO a -> IO [a]"
36+
37+
let eval_opts = initEvalOpts (hsc_dflags hsc_env) EvalStepNone
38+
interp = hscInterp hsc_env
39+
40+
liftIO (evalStmt interp eval_opts $ (EvalThis fmap_list_fv) `EvalApp` ((EvalThis fref) `EvalApp` (EvalThis aref)))
41+
>>= liftIO . handleSingStatus
42+
43+
-- | Handle the 'EvalStatus_' of an evaluation using 'EvalStepNone' which returns a single value
44+
handleSingStatus :: MonadFail m => EvalStatus_ [ForeignHValue] [HValueRef] -> m ForeignHValue
45+
handleSingStatus status =
46+
case status of
47+
EvalComplete _ (EvalSuccess [res]) -> pure res
48+
EvalComplete _ (EvalSuccess []) ->
49+
fail "evaluation did not bind any values"
50+
EvalComplete _ (EvalSuccess (_:_:_)) ->
51+
fail "evaluation produced more than one value"
52+
EvalComplete _ (EvalException e) ->
53+
fail ("evaluation raised an exception: " ++ show e)
54+
EvalBreak {} ->
55+
--TODO: Could we accidentally hit this if we set a breakpoint regardless of whether EvalStep=None? perhaps.
56+
fail "evaluation unexpectedly hit a breakpoint"
57+
58+
-- | Handle the 'EvalStatus_' of an evaluation using 'EvalStepNone' which returns a list of values
59+
handleMultiStatus :: MonadFail m => EvalStatus_ [ForeignHValue] [HValueRef] -> m [ForeignHValue]
60+
handleMultiStatus status =
61+
case status of
62+
EvalComplete _ (EvalSuccess res) -> pure res
63+
EvalComplete _ (EvalException e) ->
64+
fail ("evaluation raised an exception: " ++ show e)
65+
-- TODO?: throwIO (fromSerializableException e)
66+
EvalBreak {} ->
67+
fail "evaluation unexpectedly hit a breakpoint"

haskell-debugger/GHC/Debugger/Runtime/Term/Parser.hs

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,10 @@ import qualified GHC.Debugger.Logger as Logger
1919
import GHC.Utils.Outputable (text, (<+>), ppr)
2020
import Control.Monad.Reader
2121
import GHC.Core.TyCo.Compare
22-
import GHC.Driver.Config
2322
import GHC.Stack
24-
import GHCi.RemoteTypes
25-
26-
2723

2824
import GHC.Debugger.Monad
25+
import GHC.Debugger.Runtime.Eval
2926

3027
-- | The main entry point for running the 'TermParser'.
3128
obtainParsedTerm
@@ -236,34 +233,6 @@ foreignValueToTerm ty fhv =
236233
hsc_env <- getSession
237234
liftIO $ cvObtainTerm hsc_env 2 False ty fhv
238235

239-
--------------------------------------------------------------------------------
240-
-- * Evaluation
241-
--------------------------------------------------------------------------------
242-
243-
-- | Evaluate `f x`.
244-
evalApplication :: ForeignHValue -> ForeignHValue -> Debugger (EvalStatus_ [ForeignHValue] [HValueRef])
245-
evalApplication fref aref = do
246-
hsc_env <- getSession
247-
mk_list_fv <- compileExprRemote "(pure @IO . (:[])) :: a -> IO [a]"
248-
249-
let eval_opts = initEvalOpts (hsc_dflags hsc_env) EvalStepNone
250-
interp = hscInterp hsc_env
251-
252-
liftIO $ (evalStmt interp eval_opts $ (EvalThis mk_list_fv) `EvalApp` ((EvalThis fref) `EvalApp` (EvalThis aref)))
253-
254-
handleStatusParser :: EvalStatus_ [ForeignHValue] [HValueRef] -> TermParser ForeignHValue
255-
handleStatusParser status =
256-
case status of
257-
EvalComplete _ (EvalSuccess [res]) -> pure res
258-
EvalComplete _ (EvalSuccess []) ->
259-
parseError (TermParseError "evaluation did not bind any values")
260-
EvalComplete _ (EvalSuccess (_:_:_)) ->
261-
parseError (TermParseError "evaluation produced more than one value")
262-
EvalComplete _ (EvalException e) ->
263-
parseError (TermParseError ("evaluation raised an exception: " ++ show e))
264-
EvalBreak {} ->
265-
parseError (TermParseError "evaluation unexpectedly hit a breakpoint")
266-
267236
--------------------------------------------------------------------------------
268237
-- * Logging parsers
269238
--------------------------------------------------------------------------------
@@ -378,8 +347,7 @@ programTermParser =
378347
let fref1 = val p1
379348
let fref2 = val p2
380349
let (_, _arg_ty, res_ty) = splitFunTy (termType p1)
381-
eval_status <- liftDebugger $ evalApplication fref1 fref2
382-
res <- handleStatusParser eval_status
350+
res <- liftDebugger $ evalApplication fref1 fref2
383351
foreignValueToTerm res_ty res
384352

385353
programBranchParser = do

0 commit comments

Comments
 (0)