module InteractiveEval (
#ifdef GHCI
RunResult(..), Status(..), Resume(..), History(..),
runStmt, runStmtWithLocation, runDecls, runDeclsWithLocation,
parseImportDecl, SingleStep(..),
resume,
abandon, abandonAll,
getResumeContext,
getHistorySpan,
getModBreaks,
getHistoryModule,
back, forward,
setContext, getContext,
availsToGlobalRdrEnv,
getNamesInScope,
getRdrNamesInScope,
moduleIsInterpreted,
getInfo,
exprType,
typeKind,
parseName,
showModule,
isModuleInterpreted,
compileExpr, dynCompileExpr,
Term(..), obtainTermFromId, obtainTermFromVal, reconstructType
#endif
) where
#ifdef GHCI
#include "HsVersions.h"
import GhcMonad
import HscMain
import HsSyn
import HscTypes
import InstEnv
import TyCon
import Type hiding( typeKind )
import TcType hiding( typeKind )
import Var
import Id
import Name hiding ( varName )
import NameSet
import Avail
import RdrName
import VarSet
import VarEnv
import ByteCodeInstr
import Linker
import DynFlags
import Unique
import UniqSupply
import Module
import Panic
import UniqFM
import Maybes
import ErrUtils
import SrcLoc
import BreakArray
import RtClosureInspect
import Outputable
import FastString
import MonadUtils
import System.Directory
import Data.Dynamic
import Data.Either
import Data.List (find)
import Control.Monad
#if __GLASGOW_HASKELL__ >= 701
import Foreign.Safe
#else
import Foreign hiding (unsafePerformIO)
#endif
import Foreign.C
import GHC.Exts
import Data.Array
import Exception
import Control.Concurrent
import System.IO.Unsafe
data RunResult
= RunOk [Name]
| RunException SomeException
| RunBreak ThreadId [Name] (Maybe BreakInfo)
data Status
= Break Bool HValue BreakInfo ThreadId
| Complete (Either SomeException [HValue])
data Resume
= Resume {
resumeStmt :: String,
resumeThreadId :: ThreadId,
resumeBreakMVar :: MVar (),
resumeStatMVar :: MVar Status,
resumeBindings :: ([TyThing], GlobalRdrEnv),
resumeFinalIds :: [Id],
resumeApStack :: HValue,
resumeBreakInfo :: Maybe BreakInfo,
resumeSpan :: SrcSpan,
resumeHistory :: [History],
resumeHistoryIx :: Int
}
getResumeContext :: GhcMonad m => m [Resume]
getResumeContext = withSession (return . ic_resume . hsc_IC)
data SingleStep
= RunToCompletion
| SingleStep
| RunAndLogSteps
isStep :: SingleStep -> Bool
isStep RunToCompletion = False
isStep _ = True
data History
= History {
historyApStack :: HValue,
historyBreakInfo :: BreakInfo,
historyEnclosingDecls :: [String]
}
mkHistory :: HscEnv -> HValue -> BreakInfo -> History
mkHistory hsc_env hval bi = let
decls = findEnclosingDecls hsc_env bi
in History hval bi decls
getHistoryModule :: History -> Module
getHistoryModule = breakInfo_module . historyBreakInfo
getHistorySpan :: HscEnv -> History -> SrcSpan
getHistorySpan hsc_env hist =
let inf = historyBreakInfo hist
num = breakInfo_number inf
in case lookupUFM (hsc_HPT hsc_env) (moduleName (breakInfo_module inf)) of
Just hmi -> modBreaks_locs (getModBreaks hmi) ! num
_ -> panic "getHistorySpan"
getModBreaks :: HomeModInfo -> ModBreaks
getModBreaks hmi
| Just linkable <- hm_linkable hmi,
[BCOs _ modBreaks] <- linkableUnlinked linkable
= modBreaks
| otherwise
= emptyModBreaks
findEnclosingDecls :: HscEnv -> BreakInfo -> [String]
findEnclosingDecls hsc_env inf =
let hmi = expectJust "findEnclosingDecls" $
lookupUFM (hsc_HPT hsc_env) (moduleName $ breakInfo_module inf)
mb = getModBreaks hmi
in modBreaks_decls mb ! breakInfo_number inf
updateFixityEnv :: GhcMonad m => FixityEnv -> m ()
updateFixityEnv fix_env = do
hsc_env <- getSession
let ic = hsc_IC hsc_env
setSession $ hsc_env { hsc_IC = ic { ic_fix_env = fix_env } }
runStmt :: GhcMonad m => String -> SingleStep -> m RunResult
runStmt = runStmtWithLocation "<interactive>" 1
runStmtWithLocation :: GhcMonad m => String -> Int ->
String -> SingleStep -> m RunResult
runStmtWithLocation source linenumber expr step =
do
hsc_env <- getSession
breakMVar <- liftIO $ newEmptyMVar
statusMVar <- liftIO $ newEmptyMVar
let ic = hsc_IC hsc_env
idflags' = ic_dflags ic `wopt_unset` Opt_WarnUnusedBinds
hsc_env' = hsc_env{ hsc_IC = ic{ ic_dflags = idflags' } }
r <- liftIO $ hscStmtWithLocation hsc_env' expr source linenumber
case r of
Nothing -> return (RunOk [])
Just (tyThings, hval, fix_env) -> do
updateFixityEnv fix_env
status <-
withVirtualCWD $
withBreakAction (isStep step) idflags' breakMVar statusMVar $ do
liftIO $ sandboxIO idflags' statusMVar hval
let ic = hsc_IC hsc_env
bindings = (ic_tythings ic, ic_rn_gbl_env ic)
case step of
RunAndLogSteps ->
traceRunStatus expr bindings tyThings
breakMVar statusMVar status emptyHistory
_other ->
handleRunStatus expr bindings tyThings
breakMVar statusMVar status emptyHistory
runDecls :: GhcMonad m => String -> m [Name]
runDecls = runDeclsWithLocation "<interactive>" 1
runDeclsWithLocation :: GhcMonad m => String -> Int -> String -> m [Name]
runDeclsWithLocation source linenumber expr =
do
hsc_env <- getSession
(tyThings, ic) <- liftIO $ hscDeclsWithLocation hsc_env expr source linenumber
setSession $ hsc_env { hsc_IC = ic }
hsc_env <- getSession
hsc_env' <- liftIO $ rttiEnvironment hsc_env
modifySession (\_ -> hsc_env')
return (map getName tyThings)
withVirtualCWD :: GhcMonad m => m a -> m a
withVirtualCWD m = do
hsc_env <- getSession
let ic = hsc_IC hsc_env
let set_cwd = do
dir <- liftIO $ getCurrentDirectory
case ic_cwd ic of
Just dir -> liftIO $ setCurrentDirectory dir
Nothing -> return ()
return dir
reset_cwd orig_dir = do
virt_dir <- liftIO $ getCurrentDirectory
hsc_env <- getSession
let old_IC = hsc_IC hsc_env
setSession hsc_env{ hsc_IC = old_IC{ ic_cwd = Just virt_dir } }
liftIO $ setCurrentDirectory orig_dir
gbracket set_cwd reset_cwd $ \_ -> m
parseImportDecl :: GhcMonad m => String -> m (ImportDecl RdrName)
parseImportDecl expr = withSession $ \hsc_env -> liftIO $ hscImport hsc_env expr
emptyHistory :: BoundedList History
emptyHistory = nilBL 50
handleRunStatus :: GhcMonad m =>
String-> ([TyThing],GlobalRdrEnv) -> [Id]
-> MVar () -> MVar Status -> Status -> BoundedList History
-> m RunResult
handleRunStatus expr bindings final_ids breakMVar statusMVar status
history =
case status of
(Break is_exception apStack info tid) -> do
hsc_env <- getSession
let mb_info | is_exception = Nothing
| otherwise = Just info
(hsc_env1, names, span) <- liftIO $ bindLocalsAtBreakpoint hsc_env apStack
mb_info
let
resume = Resume { resumeStmt = expr, resumeThreadId = tid
, resumeBreakMVar = breakMVar, resumeStatMVar = statusMVar
, resumeBindings = bindings, resumeFinalIds = final_ids
, resumeApStack = apStack, resumeBreakInfo = mb_info
, resumeSpan = span, resumeHistory = toListBL history
, resumeHistoryIx = 0 }
hsc_env2 = pushResume hsc_env1 resume
modifySession (\_ -> hsc_env2)
return (RunBreak tid names mb_info)
(Complete either_hvals) ->
case either_hvals of
Left e -> return (RunException e)
Right hvals -> do
hsc_env <- getSession
let final_ic = extendInteractiveContext (hsc_IC hsc_env)
(map AnId final_ids)
final_names = map getName final_ids
liftIO $ Linker.extendLinkEnv (zip final_names hvals)
hsc_env' <- liftIO $ rttiEnvironment hsc_env{hsc_IC=final_ic}
modifySession (\_ -> hsc_env')
return (RunOk final_names)
traceRunStatus :: GhcMonad m =>
String -> ([TyThing], GlobalRdrEnv) -> [Id]
-> MVar () -> MVar Status -> Status -> BoundedList History
-> m RunResult
traceRunStatus expr bindings final_ids
breakMVar statusMVar status history = do
hsc_env <- getSession
case status of
(Break is_exception apStack info tid) | not is_exception -> do
b <- liftIO $ isBreakEnabled hsc_env info
if b
then handle_normally
else do
let history' = mkHistory hsc_env apStack info `consBL` history
_ <- liftIO $ evaluate history'
status <-
withBreakAction True (hsc_dflags hsc_env)
breakMVar statusMVar $ do
liftIO $ withInterruptsSentTo tid $ do
putMVar breakMVar ()
takeMVar statusMVar
traceRunStatus expr bindings final_ids
breakMVar statusMVar status history'
_other ->
handle_normally
where
handle_normally = handleRunStatus expr bindings final_ids
breakMVar statusMVar status history
isBreakEnabled :: HscEnv -> BreakInfo -> IO Bool
isBreakEnabled hsc_env inf =
case lookupUFM (hsc_HPT hsc_env) (moduleName (breakInfo_module inf)) of
Just hmi -> do
w <- getBreak (modBreaks_flags (getModBreaks hmi))
(breakInfo_number inf)
case w of Just n -> return (n /= 0); _other -> return False
_ ->
return False
foreign import ccall "&rts_stop_next_breakpoint" stepFlag :: Ptr CInt
foreign import ccall "&rts_stop_on_exception" exceptionFlag :: Ptr CInt
setStepFlag :: IO ()
setStepFlag = poke stepFlag 1
resetStepFlag :: IO ()
resetStepFlag = poke stepFlag 0
foreign import ccall "&rts_breakpoint_io_action"
breakPointIOAction :: Ptr (StablePtr (Bool -> BreakInfo -> HValue -> IO ()))
sandboxIO :: DynFlags -> MVar Status -> IO [HValue] -> IO Status
sandboxIO dflags statusMVar thing =
mask $ \restore ->
let runIt = liftM Complete $ try (restore $ rethrow dflags thing)
in if dopt Opt_GhciSandbox dflags
then do tid <- forkIO $ do res <- runIt
putMVar statusMVar res
withInterruptsSentTo tid $ takeMVar statusMVar
else
runIt
rethrow :: DynFlags -> IO a -> IO a
rethrow dflags io = Exception.catch io $ \se -> do
if dopt Opt_BreakOnError dflags &&
not (dopt Opt_BreakOnException dflags)
then poke exceptionFlag 1
else case fromException se of
Just UserInterrupt -> return ()
_ -> poke exceptionFlag 0
Exception.throwIO se
withInterruptsSentTo :: ThreadId -> IO r -> IO r
withInterruptsSentTo thread get_result = do
bracket (pushInterruptTargetThread thread)
(\_ -> popInterruptTargetThread)
(\_ -> get_result)
withBreakAction :: (ExceptionMonad m, MonadIO m) =>
Bool -> DynFlags -> MVar () -> MVar Status -> m a -> m a
withBreakAction step dflags breakMVar statusMVar act
= gbracket (liftIO setBreakAction) (liftIO . resetBreakAction) (\_ -> act)
where
setBreakAction = do
stablePtr <- newStablePtr onBreak
poke breakPointIOAction stablePtr
when (dopt Opt_BreakOnException dflags) $ poke exceptionFlag 1
when step $ setStepFlag
return stablePtr
onBreak is_exception info apStack = do
tid <- myThreadId
putMVar statusMVar (Break is_exception apStack info tid)
takeMVar breakMVar
resetBreakAction stablePtr = do
poke breakPointIOAction noBreakStablePtr
poke exceptionFlag 0
resetStepFlag
freeStablePtr stablePtr
noBreakStablePtr :: StablePtr (Bool -> BreakInfo -> HValue -> IO ())
noBreakStablePtr = unsafePerformIO $ newStablePtr noBreakAction
noBreakAction :: Bool -> BreakInfo -> HValue -> IO ()
noBreakAction False _ _ = putStrLn "*** Ignoring breakpoint"
noBreakAction True _ _ = return ()
resume :: GhcMonad m => (SrcSpan->Bool) -> SingleStep -> m RunResult
resume canLogSpan step
= do
hsc_env <- getSession
let ic = hsc_IC hsc_env
resume = ic_resume ic
case resume of
[] -> ghcError (ProgramError "not stopped at a breakpoint")
(r:rs) -> do
let (resume_tmp_te,resume_rdr_env) = resumeBindings r
ic' = ic { ic_tythings = resume_tmp_te,
ic_rn_gbl_env = resume_rdr_env,
ic_resume = rs }
modifySession (\_ -> hsc_env{ hsc_IC = ic' })
let new_names = map getName (filter (`notElem` resume_tmp_te)
(ic_tythings ic))
liftIO $ Linker.deleteFromLinkEnv new_names
when (isStep step) $ liftIO setStepFlag
case r of
Resume { resumeStmt = expr, resumeThreadId = tid
, resumeBreakMVar = breakMVar, resumeStatMVar = statusMVar
, resumeBindings = bindings, resumeFinalIds = final_ids
, resumeApStack = apStack, resumeBreakInfo = info, resumeSpan = span
, resumeHistory = hist } -> do
withVirtualCWD $ do
withBreakAction (isStep step) (hsc_dflags hsc_env)
breakMVar statusMVar $ do
status <- liftIO $ withInterruptsSentTo tid $ do
putMVar breakMVar ()
takeMVar statusMVar
let prevHistoryLst = fromListBL 50 hist
hist' = case info of
Nothing -> prevHistoryLst
Just i
| not $canLogSpan span -> prevHistoryLst
| otherwise -> mkHistory hsc_env apStack i `consBL`
fromListBL 50 hist
case step of
RunAndLogSteps ->
traceRunStatus expr bindings final_ids
breakMVar statusMVar status hist'
_other ->
handleRunStatus expr bindings final_ids
breakMVar statusMVar status hist'
back :: GhcMonad m => m ([Name], Int, SrcSpan)
back = moveHist (+1)
forward :: GhcMonad m => m ([Name], Int, SrcSpan)
forward = moveHist (subtract 1)
moveHist :: GhcMonad m => (Int -> Int) -> m ([Name], Int, SrcSpan)
moveHist fn = do
hsc_env <- getSession
case ic_resume (hsc_IC hsc_env) of
[] -> ghcError (ProgramError "not stopped at a breakpoint")
(r:rs) -> do
let ix = resumeHistoryIx r
history = resumeHistory r
new_ix = fn ix
when (new_ix > length history) $
ghcError (ProgramError "no more logged breakpoints")
when (new_ix < 0) $
ghcError (ProgramError "already at the beginning of the history")
let
update_ic apStack mb_info = do
(hsc_env1, names, span) <- liftIO $ bindLocalsAtBreakpoint hsc_env
apStack mb_info
let ic = hsc_IC hsc_env1
r' = r { resumeHistoryIx = new_ix }
ic' = ic { ic_resume = r':rs }
modifySession (\_ -> hsc_env1{ hsc_IC = ic' })
return (names, new_ix, span)
if new_ix == 0
then case r of
Resume { resumeApStack = apStack,
resumeBreakInfo = mb_info } ->
update_ic apStack mb_info
else case history !! (new_ix 1) of
History apStack info _ ->
update_ic apStack (Just info)
result_fs :: FastString
result_fs = fsLit "_result"
bindLocalsAtBreakpoint
:: HscEnv
-> HValue
-> Maybe BreakInfo
-> IO (HscEnv, [Name], SrcSpan)
bindLocalsAtBreakpoint hsc_env apStack Nothing = do
let exn_fs = fsLit "_exception"
exn_name = mkInternalName (getUnique exn_fs) (mkVarOccFS exn_fs) span
e_fs = fsLit "e"
e_name = mkInternalName (getUnique e_fs) (mkTyVarOccFS e_fs) span
e_tyvar = mkRuntimeUnkTyVar e_name liftedTypeKind
exn_id = AnId $ Id.mkVanillaGlobal exn_name (mkTyVarTy e_tyvar)
ictxt0 = hsc_IC hsc_env
ictxt1 = extendInteractiveContext ictxt0 [exn_id]
span = mkGeneralSrcSpan (fsLit "<exception thrown>")
Linker.extendLinkEnv [(exn_name, unsafeCoerce# apStack)]
return (hsc_env{ hsc_IC = ictxt1 }, [exn_name], span)
bindLocalsAtBreakpoint hsc_env apStack (Just info) = do
let
mod_name = moduleName (breakInfo_module info)
hmi = expectJust "bindLocalsAtBreakpoint" $
lookupUFM (hsc_HPT hsc_env) mod_name
breaks = getModBreaks hmi
index = breakInfo_number info
vars = breakInfo_vars info
result_ty = breakInfo_resty info
occs = modBreaks_vars breaks ! index
span = modBreaks_locs breaks ! index
pointers = filter (\(id,_) -> isPointer id) vars
isPointer id | UnaryRep ty <- repType (idType id)
, PtrRep <- typePrimRep ty = True
| otherwise = False
(ids, offsets) = unzip pointers
free_tvs = foldr (unionVarSet . tyVarsOfType . idType)
(tyVarsOfType result_ty) ids
mb_hValues <- mapM (getIdValFromApStack apStack) (map fromIntegral offsets)
let filtered_ids = [ id | (id, Just _hv) <- zip ids mb_hValues ]
when (any isNothing mb_hValues) $
debugTraceMsg (hsc_dflags hsc_env) 1 $
text "Warning: _result has been evaluated, some bindings have been lost"
us <- mkSplitUniqSupply 'I'
let (us1, us2) = splitUniqSupply us
tv_subst = newTyVars us1 free_tvs
new_ids = zipWith3 (mkNewId tv_subst) occs filtered_ids (uniqsFromSupply us2)
names = map idName new_ids
let result_name = mkInternalName (getUnique result_fs)
(mkVarOccFS result_fs) span
result_id = Id.mkVanillaGlobal result_name (substTy tv_subst result_ty)
let result_ok = isPointer result_id
all_ids | result_ok = result_id : new_ids
| otherwise = new_ids
id_tys = map idType all_ids
(_,tidy_tys) = tidyOpenTypes emptyTidyEnv id_tys
final_ids = zipWith setIdType all_ids tidy_tys
ictxt0 = hsc_IC hsc_env
ictxt1 = extendInteractiveContext ictxt0 (map AnId final_ids)
Linker.extendLinkEnv [ (name,hval) | (name, Just hval) <- zip names mb_hValues ]
when result_ok $ Linker.extendLinkEnv [(result_name, unsafeCoerce# apStack)]
hsc_env1 <- rttiEnvironment hsc_env{ hsc_IC = ictxt1 }
return (hsc_env1, if result_ok then result_name:names else names, span)
where
mkNewId :: TvSubst -> OccName -> Id -> Unique -> Id
mkNewId tv_subst occ id uniq
= Id.mkVanillaGlobalWithInfo name ty (idInfo id)
where
loc = nameSrcSpan (idName id)
name = mkInternalName uniq occ loc
ty = substTy tv_subst (idType id)
newTyVars :: UniqSupply -> TcTyVarSet -> TvSubst
newTyVars us tvs
= mkTopTvSubst [ (tv, mkTyVarTy (mkRuntimeUnkTyVar name (tyVarKind tv)))
| (tv, uniq) <- varSetElems tvs `zip` uniqsFromSupply us
, let name = setNameUnique (tyVarName tv) uniq ]
rttiEnvironment :: HscEnv -> IO HscEnv
rttiEnvironment hsc_env@HscEnv{hsc_IC=ic} = do
let tmp_ids = [id | AnId id <- ic_tythings ic]
incompletelyTypedIds =
[id | id <- tmp_ids
, not $ noSkolems id
, (occNameFS.nameOccName.idName) id /= result_fs]
hsc_env' <- foldM improveTypes hsc_env (map idName incompletelyTypedIds)
return hsc_env'
where
noSkolems = isEmptyVarSet . tyVarsOfType . idType
improveTypes hsc_env@HscEnv{hsc_IC=ic} name = do
let tmp_ids = [id | AnId id <- ic_tythings ic]
Just id = find (\i -> idName i == name) tmp_ids
if noSkolems id
then return hsc_env
else do
mb_new_ty <- reconstructType hsc_env 10 id
let old_ty = idType id
case mb_new_ty of
Nothing -> return hsc_env
Just new_ty -> do
case improveRTTIType hsc_env old_ty new_ty of
Nothing -> return $
WARN(True, text (":print failed to calculate the "
++ "improvement for a type")) hsc_env
Just subst -> do
let dflags = hsc_dflags hsc_env
when (dopt Opt_D_dump_rtti dflags) $
printInfoForUser dflags alwaysQualify $
fsep [text "RTTI Improvement for", ppr id, equals, ppr subst]
let ic' = extendInteractiveContext
(substInteractiveContext ic subst) []
return hsc_env{hsc_IC=ic'}
getIdValFromApStack :: HValue -> Int -> IO (Maybe HValue)
getIdValFromApStack apStack (I# stackDepth) = do
case getApStackVal# apStack (stackDepth +# 1#) of
(# ok, result #) ->
case ok of
0# -> return Nothing
_ -> return (Just (unsafeCoerce# result))
pushResume :: HscEnv -> Resume -> HscEnv
pushResume hsc_env resume = hsc_env { hsc_IC = ictxt1 }
where
ictxt0 = hsc_IC hsc_env
ictxt1 = ictxt0 { ic_resume = resume : ic_resume ictxt0 }
abandon :: GhcMonad m => m Bool
abandon = do
hsc_env <- getSession
let ic = hsc_IC hsc_env
resume = ic_resume ic
case resume of
[] -> return False
r:rs -> do
modifySession $ \_ -> hsc_env{ hsc_IC = ic { ic_resume = rs } }
liftIO $ abandon_ r
return True
abandonAll :: GhcMonad m => m Bool
abandonAll = do
hsc_env <- getSession
let ic = hsc_IC hsc_env
resume = ic_resume ic
case resume of
[] -> return False
rs -> do
modifySession $ \_ -> hsc_env{ hsc_IC = ic { ic_resume = [] } }
liftIO $ mapM_ abandon_ rs
return True
abandon_ :: Resume -> IO ()
abandon_ r = do
killThread (resumeThreadId r)
putMVar (resumeBreakMVar r) ()
_ <- takeMVar (resumeStatMVar r)
return ()
data BoundedList a = BL
!Int
!Int
[a]
[a]
nilBL :: Int -> BoundedList a
nilBL bound = BL 0 bound [] []
consBL :: a -> BoundedList a -> BoundedList a
consBL a (BL len bound left right)
| len < bound = BL (len+1) bound (a:left) right
| null right = BL len bound [a] $! tail (reverse left)
| otherwise = BL len bound (a:left) $! tail right
toListBL :: BoundedList a -> [a]
toListBL (BL _ _ left right) = left ++ reverse right
fromListBL :: Int -> [a] -> BoundedList a
fromListBL bound l = BL (length l) bound l []
setContext :: GhcMonad m => [InteractiveImport] -> m ()
setContext imports
= do { hsc_env <- getSession
; let dflags = hsc_dflags hsc_env
; all_env_err <- liftIO $ findGlobalRdrEnv hsc_env imports
; case all_env_err of
Left (mod, err) -> ghcError (formatError dflags mod err)
Right all_env -> do {
; let old_ic = hsc_IC hsc_env
final_rdr_env = ic_tythings old_ic `icPlusGblRdrEnv` all_env
; modifySession $ \_ ->
hsc_env{ hsc_IC = old_ic { ic_imports = imports
, ic_rn_gbl_env = final_rdr_env }}}}
where
formatError dflags mod err = ProgramError . showSDoc dflags $
text "Cannot add module" <+> ppr mod <+>
text "to context:" <+> text err
findGlobalRdrEnv :: HscEnv -> [InteractiveImport]
-> IO (Either (ModuleName, String) GlobalRdrEnv)
findGlobalRdrEnv hsc_env imports
= do { idecls_env <- hscRnImportDecls hsc_env idecls
; return $ case partitionEithers (map mkEnv imods) of
([], imods_env) -> Right (foldr plusGlobalRdrEnv idecls_env imods_env)
(err : _, _) -> Left err }
where
idecls :: [LImportDecl RdrName]
idecls = [noLoc d | IIDecl d <- imports]
imods :: [ModuleName]
imods = [m | IIModule m <- imports]
mkEnv mod = case mkTopLevEnv (hsc_HPT hsc_env) mod of
Left err -> Left (mod, err)
Right env -> Right env
availsToGlobalRdrEnv :: ModuleName -> [AvailInfo] -> GlobalRdrEnv
availsToGlobalRdrEnv mod_name avails
= mkGlobalRdrEnv (gresFromAvails imp_prov avails)
where
imp_prov = Imported [ImpSpec { is_decl = decl, is_item = ImpAll}]
decl = ImpDeclSpec { is_mod = mod_name, is_as = mod_name,
is_qual = False,
is_dloc = srcLocSpan interactiveSrcLoc }
mkTopLevEnv :: HomePackageTable -> ModuleName -> Either String GlobalRdrEnv
mkTopLevEnv hpt modl
= case lookupUFM hpt modl of
Nothing -> Left "not a home module"
Just details ->
case mi_globals (hm_iface details) of
Nothing -> Left "not interpreted"
Just env -> Right env
getContext :: GhcMonad m => m [InteractiveImport]
getContext = withSession $ \HscEnv{ hsc_IC=ic } ->
return (ic_imports ic)
moduleIsInterpreted :: GhcMonad m => Module -> m Bool
moduleIsInterpreted modl = withSession $ \h ->
if modulePackageId modl /= thisPackage (hsc_dflags h)
then return False
else case lookupUFM (hsc_HPT h) (moduleName modl) of
Just details -> return (isJust (mi_globals (hm_iface details)))
_not_a_home_module -> return False
getInfo :: GhcMonad m => Name -> m (Maybe (TyThing,Fixity,[ClsInst]))
getInfo name
= withSession $ \hsc_env ->
do mb_stuff <- liftIO $ hscTcRnGetInfo hsc_env name
case mb_stuff of
Nothing -> return Nothing
Just (thing, fixity, ispecs) -> do
let rdr_env = ic_rn_gbl_env (hsc_IC hsc_env)
return (Just (thing, fixity, filter (plausible rdr_env) ispecs))
where
plausible rdr_env ispec
= all ok $ nameSetToList $ orphNamesOfType $ idType $ instanceDFunId ispec
where
ok n | n == name = True
| isBuiltInSyntax n = True
| isExternalName n = any ((== n) . gre_name)
(lookupGRE_Name rdr_env n)
| otherwise = True
getNamesInScope :: GhcMonad m => m [Name]
getNamesInScope = withSession $ \hsc_env -> do
return (map gre_name (globalRdrEnvElts (ic_rn_gbl_env (hsc_IC hsc_env))))
getRdrNamesInScope :: GhcMonad m => m [RdrName]
getRdrNamesInScope = withSession $ \hsc_env -> do
let
ic = hsc_IC hsc_env
gbl_rdrenv = ic_rn_gbl_env ic
gbl_names = concatMap greToRdrNames $ globalRdrEnvElts gbl_rdrenv
return gbl_names
greToRdrNames :: GlobalRdrElt -> [RdrName]
greToRdrNames GRE{ gre_name = name, gre_prov = prov }
= case prov of
LocalDef -> [unqual]
Imported specs -> concat (map do_spec (map is_decl specs))
where
occ = nameOccName name
unqual = Unqual occ
do_spec decl_spec
| is_qual decl_spec = [qual]
| otherwise = [unqual,qual]
where qual = Qual (is_as decl_spec) occ
parseName :: GhcMonad m => String -> m [Name]
parseName str = withSession $ \hsc_env -> do
(L _ rdr_name) <- liftIO $ hscParseIdentifier hsc_env str
liftIO $ hscTcRnLookupRdrName hsc_env rdr_name
exprType :: GhcMonad m => String -> m Type
exprType expr = withSession $ \hsc_env -> do
ty <- liftIO $ hscTcExpr hsc_env expr
return $ tidyType emptyTidyEnv ty
typeKind :: GhcMonad m => Bool -> String -> m (Type, Kind)
typeKind normalise str = withSession $ \hsc_env -> do
liftIO $ hscKcType hsc_env normalise str
compileExpr :: GhcMonad m => String -> m HValue
compileExpr expr = withSession $ \hsc_env -> do
Just (ids, hval, fix_env) <- liftIO $ hscStmt hsc_env ("let __cmCompileExpr = "++expr)
updateFixityEnv fix_env
hvals <- liftIO hval
case (ids,hvals) of
([_],[hv]) -> return hv
_ -> panic "compileExpr"
dynCompileExpr :: GhcMonad m => String -> m Dynamic
dynCompileExpr expr = do
iis <- getContext
let importDecl = ImportDecl {
ideclName = noLoc (mkModuleName "Data.Dynamic"),
ideclPkgQual = Nothing,
ideclSource = False,
ideclSafe = False,
ideclQualified = True,
ideclImplicit = False,
ideclAs = Nothing,
ideclHiding = Nothing
}
setContext (IIDecl importDecl : iis)
let stmt = "let __dynCompileExpr = Data.Dynamic.toDyn (" ++ expr ++ ")"
Just (ids, hvals, fix_env) <- withSession $ \hsc_env ->
liftIO $ hscStmt hsc_env stmt
setContext iis
updateFixityEnv fix_env
vals <- liftIO (unsafeCoerce# hvals :: IO [Dynamic])
case (ids,vals) of
(_:[], v:[]) -> return v
_ -> panic "dynCompileExpr"
showModule :: GhcMonad m => ModSummary -> m String
showModule mod_summary =
withSession $ \hsc_env -> do
interpreted <- isModuleInterpreted mod_summary
let dflags = hsc_dflags hsc_env
return (showModMsg dflags (hscTarget dflags) interpreted mod_summary)
isModuleInterpreted :: GhcMonad m => ModSummary -> m Bool
isModuleInterpreted mod_summary = withSession $ \hsc_env ->
case lookupUFM (hsc_HPT hsc_env) (ms_mod_name mod_summary) of
Nothing -> panic "missing linkable"
Just mod_info -> return (not obj_linkable)
where
obj_linkable = isObjectLinkable (expectJust "showModule" (hm_linkable mod_info))
obtainTermFromVal :: HscEnv -> Int -> Bool -> Type -> a -> IO Term
obtainTermFromVal hsc_env bound force ty x =
cvObtainTerm hsc_env bound force ty (unsafeCoerce# x)
obtainTermFromId :: HscEnv -> Int -> Bool -> Id -> IO Term
obtainTermFromId hsc_env bound force id = do
hv <- Linker.getHValue hsc_env (varName id)
cvObtainTerm hsc_env bound force (idType id) hv
reconstructType :: HscEnv -> Int -> Id -> IO (Maybe Type)
reconstructType hsc_env bound id = do
hv <- Linker.getHValue hsc_env (varName id)
cvReconstructType hsc_env bound (idType id) hv
mkRuntimeUnkTyVar :: Name -> Kind -> TyVar
mkRuntimeUnkTyVar name kind = mkTcTyVar name kind RuntimeUnk
#endif /* GHCI */