module Hint.Sandbox ( sandboxed ) where import Hint.Base import Hint.Context import Hint.Configuration import Hint.Util import {-# SOURCE #-} Hint.Typecheck ( typeChecks_unsandboxed ) import Control.Monad.Catch sandboxed :: MonadInterpreter m => (Expr -> m a) -> (Expr -> m a) sandboxed = if ghcVersion >= 610 then id else old_sandboxed old_sandboxed :: MonadInterpreter m => (Expr -> m a) -> (Expr -> m a) old_sandboxed do_stuff = \expr -> do no_sandbox <- fromConf all_mods_in_scope if no_sandbox then do_stuff expr else usingAModule do_stuff expr usingAModule :: MonadInterpreter m => (Expr -> m a) -> (Expr -> m a) usingAModule do_stuff_on = \expr -> -- -- To avoid defaulting, we will evaluate this expression without the -- monomorphism-restriction. This means that expressions that normally -- would not typecheck, suddenly will. Thus, we first check if the -- expression typechecks as is. If it doesn't, there is no need in -- going on (if it does, it may not typecheck once we restrict the -- context; that is the whole idea of this!) -- do type_checks <- typeChecks_unsandboxed expr case type_checks of False -> do_stuff_on expr -- fail as you wish... True -> do (loaded, imports) <- allModulesInContext zombies <- fromState zombie_phantoms quals <- fromState qual_imports -- let e = safeBndFor expr let mod_text no_prel mod_name = textify [ ["{-# LANGUAGE NoMonomorphismRestriction #-}"], ["{-# LANGUAGE NoImplicitPrelude #-}" | no_prel], ["module " ++ mod_name], ["where"], ["import " ++ m | m <- loaded ++ imports, not $ m `elem` (map pm_name zombies)], ["import qualified " ++ m ++ " as " ++ q | (m,q) <- quals], [e ++ " = " ++ expr] ] -- let go no_prel = do pm <- addPhantomModule (mod_text no_prel) setTopLevelModules [pm_name pm] r <- do_stuff_on e `catchIE` (\err -> case err of WontCompile _ -> do removePhantomModule pm throwM err _ -> throwM err) removePhantomModule pm return r -- If the Prelude was not explicitly imported but implicitly -- imported in some interpreted module, then the user may -- get very unintuitive errors when turning sandboxing on. Thus -- we will import the Prelude if the operation fails... -- I guess this may lead to even more obscure errors, but -- hopefully in much less frequent situations... r <- onAnEmptyContext $ go True `catchIE` (\err -> case err of WontCompile _ -> go False _ -> throwM err) -- return r -- where textify = unlines . concat