From 8266ff33c40d05f3770c64c7943734f615c327d0 Mon Sep 17 00:00:00 2001 From: Matthias Heinzel Date: Sat, 6 Aug 2016 21:34:15 +0200 Subject: [PATCH 1/4] Small update in exception test --- test/control/exception.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/control/exception.py b/test/control/exception.py index 90d0d3d..3f4376b 100644 --- a/test/control/exception.py +++ b/test/control/exception.py @@ -263,8 +263,7 @@ def test_finally_block_raises_exception(): try: raise Exception("test") except Exception as e: - if e != None: # Test that e exists since we don't implement __str__ - print("Exists!") + print(e) # Test that the correct handler is located, even if it exists in outer scopes def test_caught_by_outside_handlers(): From 87989a729dca1f030656721d7ce08827fa4779f7 Mon Sep 17 00:00:00 2001 From: Matthias Heinzel Date: Sat, 6 Aug 2016 21:34:59 +0200 Subject: [PATCH 2/4] Add tests that show exception handling problems --- test/control/exception.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/control/exception.py b/test/control/exception.py index 3f4376b..435bb3a 100644 --- a/test/control/exception.py +++ b/test/control/exception.py @@ -340,3 +340,30 @@ def test_return_in_while(): print(test_return_in_while()) finally: print("OK!") + +# Test that only objects can be raised +try: + raise 3 +except TypeError as e: + print("TypeError1") + +# Test that only classes derived from BaseException can be raised +class T: + def __str__(self): + return "T" + +try: + raise T("e") +except TypeError: + print("TypeError2") + +# Test that only classes derived from BaseException can be caught +try: + try: + raise Exception("test") + except T: + pass +except TypeError: + print("TypeError3") +except: + print("caught something else") From c3d1248b4923bb1010d8839d65ec32ccaa4b33d2 Mon Sep 17 00:00:00 2001 From: Matthias Heinzel Date: Sat, 6 Aug 2016 20:46:01 +0200 Subject: [PATCH 3/4] Only allow to raise exceptions deriving from BaseException --- src/Hython/ExceptionHandling.hs | 39 +++++++++++++++++++++++---------- src/Hython/Interpreter.hs | 2 +- src/Hython/Statement.hs | 4 ++-- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/Hython/ExceptionHandling.hs b/src/Hython/ExceptionHandling.hs index 18d1bb6..aa025af 100644 --- a/src/Hython/ExceptionHandling.hs +++ b/src/Hython/ExceptionHandling.hs @@ -3,29 +3,44 @@ where import Control.Monad.Cont.Class (MonadCont) import Data.Text (Text) +import qualified Data.Text as T import Hython.Call (call) +import Hython.Class (isSubClass) import Hython.ControlFlow (getExceptionHandler, setCurrentException) import Hython.Environment (MonadEnv, lookupName) -import Hython.Types (MonadInterpreter, newString, Object) +import Hython.Types hiding (raise) import qualified Hython.Types as Types -raise :: MonadInterpreter m => Object -> m () -raise exception = do - handler <- getExceptionHandler - setCurrentException exception - handler exception +raiseOr :: (MonadEnv Object m, MonadInterpreter m) => m () -> Object -> m () +raiseOr raiseError exc@(Object objectInfo) = do + mBaseClass <- lookupName (T.pack "BaseException") + case mBaseClass of + Just (Class baseClassInfo) -> + if isSubClass (objectClass objectInfo) baseClassInfo + then do + handler <- getExceptionHandler + setCurrentException exc + handler exc + else raiseError + _ -> Types.raise "SystemError" "could not find BaseException class" +raiseOr raiseError _ = raiseError + +raiseExternal :: (MonadEnv Object m, MonadInterpreter m) => Object -> m () +raiseExternal = + raiseOr $ Types.raise "TypeError" "exceptions must derive from BaseException" raiseInternal :: (MonadCont m, MonadEnv Object m, MonadInterpreter m) => Text -> Text -> m () raiseInternal clsName description = do mcls <- lookupName clsName case mcls of - Just obj -> do + Just cls@(Types.Class _) -> do descriptionStr <- newString description - exception <- call obj [descriptionStr] [] - raise exception - _ -> Types.raise "TypeError" "exceptions must derive from BaseException" - - + exception <- call cls [descriptionStr] [] + raiseOr raiseError exception + _ -> raiseError + where + raiseError = Types.raise "SystemError" ("internally raised invalid exception " + ++ T.unpack clsName ++ "(\"" ++ T.unpack description ++ "\")") diff --git a/src/Hython/Interpreter.hs b/src/Hython/Interpreter.hs index 83a6ef0..62430c8 100644 --- a/src/Hython/Interpreter.hs +++ b/src/Hython/Interpreter.hs @@ -118,7 +118,7 @@ defaultExceptionHandler ex = do putStr . T.unpack . className . objectClass $ info putStr ": " putStrLn msg - _ -> liftIO $ putStrLn "o_O: raised a non-object exception" + _ -> liftIO $ putStrLn "SystemError: uncaught non-object exception" liftIO exitFailure diff --git a/src/Hython/Statement.hs b/src/Hython/Statement.hs index 84a96e3..6f868c6 100644 --- a/src/Hython/Statement.hs +++ b/src/Hython/Statement.hs @@ -19,7 +19,7 @@ import Language.Python import Hython.Builtins (isInstance, len, setAttr) import Hython.ControlFlow import Hython.Environment -import qualified Hython.ExceptionHandling as EH +import Hython.ExceptionHandling (raiseExternal) import Hython.Expression (evalExpr, evalParam) import qualified Hython.Module as Module import Hython.Ref @@ -173,7 +173,7 @@ eval (Nonlocal names) = mapM_ bindNonlocal names eval (Pass) = return () -eval (Raise expr _from) = EH.raise =<< evalExpr expr +eval (Raise expr _from) = raiseExternal =<< evalExpr expr eval (Reraise) = do mexception <- getCurrentException From f3e7cd6b98a9c23a183ce1200684cfa114523ac8 Mon Sep 17 00:00:00 2001 From: Matthias Heinzel Date: Sun, 7 Aug 2016 15:58:35 +0200 Subject: [PATCH 4/4] Only allow catching classes derived from BaseException --- src/Hython/Statement.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Hython/Statement.hs b/src/Hython/Statement.hs index 6f868c6..2c5c33b 100644 --- a/src/Hython/Statement.hs +++ b/src/Hython/Statement.hs @@ -17,6 +17,7 @@ import qualified Data.Text as T import Language.Python import Hython.Builtins (isInstance, len, setAttr) +import Hython.Class (isSubClass) import Hython.ControlFlow import Hython.Environment import Hython.ExceptionHandling (raiseExternal) @@ -201,13 +202,14 @@ eval (Try clauses block elseBlock finallyBlock) = do -- Evaluate exception handler clauses handlers <- forM clauses $ \(ExceptClause expr name handlerBlock) -> do mcls <- evalExpr expr - case mcls of - (Class cls) -> return . Just $ ExceptionHandler name cls handlerBlock - _ -> do - raise "SyntaxError" "invalid class in except block header" + mBaseClass <- lookupName (T.pack "BaseException") + case (mcls, mBaseClass) of + (Class cls, Just (Class baseClass)) | isSubClass cls baseClass -> + return . Just $ ExceptionHandler name cls handlerBlock + _ -> do + raise "TypeError" "catching classes that do not inherit from BaseException is not allowed" return Nothing - -- exception <- callCC $ \handler -> do setExceptionHandler handler