lio-0.11.2.0: LIO/Run.hs
{-# LANGUAGE Trustworthy #-}
-- | This module contains functions to launch 'LIO' computations from
-- within the 'IO' monad. These functions are not useful from within
-- 'LIO' code (but not harmful either, since their types are in the
-- 'IO' monad).
--
-- This module is intended to be imported into your Main module, for
-- use in invoking 'LIO' code. The functions are also available via
-- "LIO" and "LIO.Core", but those modules will clutter your namespace
-- with symbols you don't need in the 'IO' monad.
module LIO.Run (LIOState(..), runLIO, tryLIO, evalLIO, privInit) where
import safe Control.Exception
import safe Data.IORef
import safe LIO.Label
import LIO.TCB
-- | Execute an 'LIO' action, returning its result and the final label
-- state as a pair. Note that it returns a pair whether or not the
-- 'LIO' action throws an exception. Forcing the result value will
-- re-throw the exception, but the label state will always be valid.
--
-- See also 'evalLIO'.
runLIO :: LIO l a -> LIOState l -> IO (a, LIOState l)
runLIO (LIOTCB m) s0 = do
sp <- newIORef s0
a <- m sp `catch` \e -> return $ throw $ makeCatchable e
s1 <- readIORef sp
return (a, s1)
-- | A variant of 'runLIO' that returns results in 'Right' and
-- exceptions in 'Left', much like the standard library 'try'
-- function.
tryLIO :: LIO l a -> LIOState l -> IO (Either SomeException a, LIOState l)
tryLIO lio s0 = runLIO lio s0 >>= tryit
where tryit (a, s) = do
ea <- try (evaluate a)
return (ea, s)
-- | Given an 'LIO' computation and some initial state, return an IO
-- action which, when executed, will perform the IFC-safe LIO
-- computation.
--
-- Because untrusted code cannot execute 'IO' computations, this function
-- should only be useful within trusted code. No harm is done from
-- exposing the @evalLIO@ symbol to untrusted code. (In general,
-- untrusted code is free to produce 'IO' computations, but it cannot
-- execute them.)
--
-- Unlike 'runLIO', this function throws an exception if the
-- underlying 'LIO' action terminates with an exception.
evalLIO :: LIO l a -> LIOState l -> IO a
evalLIO lio s = do
(a, _) <- runLIO lio s
return $! a
-- | Initialize some privileges (within the 'IO' monad) that can be
-- passed to 'LIO' computations run with 'runLIO' or 'evalLIO'. This
-- is a pure function, but the result is encapsulated in 'IO' to
-- make the return value inaccessible from 'LIO' computations.
--
-- Note the same effect can be achieved using the 'PrivTCB'
-- constructor, but 'PrivTCB' is easier to misuse and is only available by
-- importing "LIO.TCB".
privInit :: (SpeaksFor p) => p -> IO (Priv p)
privInit p | isPriv p = fail "privInit called on Priv object"
| otherwise = return $ PrivTCB p