bff-0.3.1: MyInterpret.hs
{-# LANGUAGE DeriveDataTypeable #-}
module MyInterpret
( simpleInterpret
, simpleTypeOf
, catchInterpreterErrors
)
where
import Language.Haskell.Interpreter
import Language.Haskell.Interpreter.Unsafe
import Prelude hiding (catch)
import Control.Concurrent
import Control.OldException
import Control.Monad
import Control.Monad.Error
import System.Posix.Signals
import Data.Typeable (Typeable)
import Data.List
import Data.Either
-- Scoped modules
modules = [ "Data.Bff",
"SimpleTree",
"Prelude",
"Data.List"
-- "ShowQ",
-- "ShowFun",
-- "SimpleReflect",
-- "Data.Function",
]
data MyException = MyException String deriving (Typeable)
timeoutIO action = bracket
(do mainId <- myThreadId
installHandler sigXCPU (CatchOnce $ throwDynTo mainId $ MyException "Time limit exceeded.") Nothing
forkIO $ do
threadDelay (5 * 1000 * 1000)
-- Time's up. It's a good day to die.
throwDynTo mainId (MyException "Time limit exceeded")
{- why do we need that here?
- yield -- give the other thread a chance
putStrLn "Killing main thread..."
killThread mainId -- Die now, srsly.
error "Time expired"
-}
)
(killThread)
(\_ -> do
mainId <- myThreadId
mvar <- newEmptyMVar
forkIO $ (action >>= putMVar mvar) `catch`
(throwTo mainId)
ret <- takeMVar mvar
evaluate (length ret) -- make sure exceptions are handled here
return ret
)
-- myInterpreter :: String -> IO String
myInterpreter todo exp = timeoutIO $ do
when (unsafe exp) $ throwDyn (MyException "Indicators for unsafe computations found in exp")
eResult <- runInterpreter $ do
--setUseLanguageExtensions False
set [languageExtensions := []]
-- Not available in hint-3.2?
-- setOptimizations All
reset
-- no need for temporary files I hope
--setInstalledModsAreInScopeQualified True
set [installedModulesInScope := True]
unsafeSetGhcOption "-fno-monomorphism-restriction"
setImports modules
-- liftIO $ putStrLn exp
todo exp
case eResult of
Left exception -> throwDyn exception
Right result -> return result
formatInterpreterError (UnknownError s) = "Unknown Interpreter Error " ++ s
formatInterpreterError (WontCompile es) = "Could not compile code:\n" ++ unlines (map errMsg es)
formatInterpreterError (NotAllowed s) = "Not allowed here " ++ s
formatInterpreterError (GhcException e) = "GHC Exception"
{- | Return true if the String contains anywhere in it any keywords associated
with dangerous functions. Unfortunately, this blacklist leaks like a sieve
and will return many false positives (eg. 'unsafed "id \"unsafed\""' will
evaluate to True, even though the phrase \"unsafe\" appears only in a String). But it
will at least catch naive and simplistic invocations of "unsafePerformIO",
"inlinePerformIO", and "unsafeCoerce". -}
unsafe :: String -> Bool
unsafe = \z -> any (`isInfixOf` z) unsafeNames
unsafeNames :: [String]
unsafeNames = ["unsafe", "inlinePerform", "liftIO", "Coerce", "Foreign",
"Typeable", "Array", "IOBase", "Handle", "ByteString",
"Editline", "GLUT", "lock", "ObjectIO", "System.Time",
"OpenGL", "Control.Concurrent", "System.Posix",
"throw", "Dyn", "cache", "stdin", "stdout", "stderr"]
catchInterpreterErrors :: IO a -> IO (Either String a)
catchInterpreterErrors action =
flip catchDyn (return . Left . formatInterpreterError) $
flip catchDyn (\(MyException s) -> return (Left s)) $
handleJust errorCalls (return . Left) $ -- bff in Bff.hs uses these
Right `fmap` action
simpleInterpret :: String -> String -> IO String
simpleInterpret defs what =
myInterpreter eval $
"let \n" ++
unlines (map (replicate 12 ' '++) (lines defs)) ++
replicate 8 ' ' ++ "in " ++ what
simpleTypeOf :: String -> String -> IO String
simpleTypeOf defs what =
myInterpreter typeOf $
"let \n" ++
unlines (map (replicate 12 ' '++) (lines defs)) ++
replicate 8 ' ' ++ "in " ++ what