diff --git a/inline-c-cpp/cxx-src/HaskellException.cxx b/inline-c-cpp/cxx-src/HaskellException.cxx index 00fb98c..d98a983 100644 --- a/inline-c-cpp/cxx-src/HaskellException.cxx +++ b/inline-c-cpp/cxx-src/HaskellException.cxx @@ -46,25 +46,51 @@ const char* HaskellException::what() const noexcept { // // regarding how to show the type of an exception. +/* mallocs a string representing the exception type name or error condition. + + Ideally, this returns a demangled string, but it may degrade to + - a mangled string if demangling fails, + - "" if exception type info is not available, + - "" if no current exception is found. + + The responsibility for freeing the returned string falls on the caller, + such as handleForeignCatch, which passes the responsibility on to ByteString + + */ #if defined(__GNUC__) || defined(__clang__) const char* currentExceptionTypeName() { + std::type_info *type_info = abi::__cxa_current_exception_type(); + if (!type_info) + return strdup(""); + + const char *raw_name = type_info->name(); + if (!raw_name) + return strdup(""); + int demangle_status; - return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &demangle_status); + const char *demangled_name = abi::__cxa_demangle(raw_name, 0, 0, &demangle_status); + if (!demangled_name) + return strdup(raw_name); + + return demangled_name; } #endif -void setMessageOfStdException(const std::exception &e, char** msgStrPtr, char **typStrPtr){ +/* Set the message and type strings. + + The responsibility for freeing the returned string falls on the caller, + such as handleForeignCatch, which passes the responsibility on to a + ByteString. + */ +void setMessageOfStdException(const std::exception &e, const char** msgStrPtr, const char **typStrPtr){ *msgStrPtr = strdup(e.what()); setCppExceptionType(typStrPtr); } -void setCppExceptionType(char** typStrPtr){ +void setCppExceptionType(const char** typStrPtr){ #if defined(__GNUC__) || defined(__clang__) - const char* message = currentExceptionTypeName(); - size_t message_len = strlen(message) + 1; - *typStrPtr = static_cast(std::malloc(message_len)); - std::memcpy(*typStrPtr, message, message_len); + *typStrPtr = currentExceptionTypeName(); #else *typStrPtr = NULL; #endif diff --git a/inline-c-cpp/include/HaskellException.hxx b/inline-c-cpp/include/HaskellException.hxx index 6becc95..ce0c966 100644 --- a/inline-c-cpp/include/HaskellException.hxx +++ b/inline-c-cpp/include/HaskellException.hxx @@ -36,5 +36,5 @@ public: }; -void setMessageOfStdException(const std::exception &e, char** msgStrPtr, char **typeStrPtr); -void setCppExceptionType(char** typeStrPtr); +void setMessageOfStdException(const std::exception &e, const char** msgStrPtr, const char **typeStrPtr); +void setCppExceptionType(const char** typeStrPtr); diff --git a/inline-c-cpp/src/Language/C/Inline/Cpp/Exception.hs b/inline-c-cpp/src/Language/C/Inline/Cpp/Exception.hs index 9efbb09..ff09371 100644 --- a/inline-c-cpp/src/Language/C/Inline/Cpp/Exception.hs +++ b/inline-c-cpp/src/Language/C/Inline/Cpp/Exception.hs @@ -52,8 +52,15 @@ instance Exception CppException where type CppExceptionPtr = ForeignPtr AbstractCppExceptionPtr +-- | This converts a plain pointer to a managed object. +-- +-- The pointer must have been created with @new@. The returned 'CppExceptionPtr' +-- will @delete@ it when it is garbage collected, so you must not @delete@ it +-- on your own. This function is called "unsafe" because it is not memory safe +-- by itself, but safe when used correctly; similar to for example +-- 'BS.unsafePackMallocCString'. unsafeFromNewCppExceptionPtr :: Ptr AbstractCppExceptionPtr -> IO CppExceptionPtr -unsafeFromNewCppExceptionPtr p = newForeignPtr finalizeAbstractCppExceptionPtr p +unsafeFromNewCppExceptionPtr = newForeignPtr finalizeAbstractCppExceptionPtr finalizeAbstractCppExceptionPtr :: FinalizerPtr AbstractCppExceptionPtr {-# NOINLINE finalizeAbstractCppExceptionPtr #-} @@ -102,8 +109,16 @@ handleForeignCatch cont = ExTypeNoException -> return (Right res) ExTypeStdException -> do ex <- unsafeFromNewCppExceptionPtr =<< peek exPtr + + -- BS.unsafePackMallocCString: safe because setMessageOfStdException + -- (invoked via tryBlockQuoteExp) sets msgCStringPtr to a newly + -- malloced string. errMsg <- BS.unsafePackMallocCString =<< peek msgCStringPtr + + -- BS.unsafePackMallocCString: safe because currentExceptionTypeName + -- returns a newly malloced string mbExcType <- maybePeek BS.unsafePackMallocCString =<< peek typCStringPtr + return (Left (CppStdException ex errMsg mbExcType)) ExTypeHaskellException -> do haskellExPtr <- peek haskellExPtrPtr @@ -117,7 +132,11 @@ handleForeignCatch cont = return (Left (CppHaskellException someExc)) ExTypeOtherException -> do ex <- unsafeFromNewCppExceptionPtr =<< peek exPtr + + -- BS.unsafePackMallocCString: safe because currentExceptionTypeName + -- returns a newly malloced string mbExcType <- maybePeek BS.unsafePackMallocCString =<< peek typCStringPtr + return (Left (CppNonStdException ex mbExcType)) :: IO (Either CppException a) _ -> error "Unexpected C++ exception type." @@ -199,8 +218,8 @@ tryBlockQuoteExp blockStr = do let inlineCStr = unlines [ ty ++ " {" , " int* __inline_c_cpp_exception_type__ = $(int* " ++ nameBase typePtrVarName ++ ");" - , " char** __inline_c_cpp_error_message__ = $(char** " ++ nameBase msgPtrVarName ++ ");" - , " char** __inline_c_cpp_error_typ__ = $(char** " ++ nameBase typeStrPtrVarName ++ ");" + , " const char** __inline_c_cpp_error_message__ = $(const char** " ++ nameBase msgPtrVarName ++ ");" + , " const char** __inline_c_cpp_error_typ__ = $(const char** " ++ nameBase typeStrPtrVarName ++ ");" , " HaskellException** __inline_c_cpp_haskellexception__ = (HaskellException**)($(void ** " ++ nameBase haskellExPtrVarName ++ "));" , " std::exception_ptr** __inline_c_cpp_exception_ptr__ = (std::exception_ptr**)$(std::exception_ptr** " ++ nameBase exPtrVarName ++ ");" , " try {" diff --git a/inline-c-cpp/test/tests.hs b/inline-c-cpp/test/tests.hs index 2262247..9f8348e 100644 --- a/inline-c-cpp/test/tests.hs +++ b/inline-c-cpp/test/tests.hs @@ -120,6 +120,13 @@ main = Hspec.hspec $ do result `shouldBeCppOtherException` (Just "unsigned int") + Hspec.it "non-exceptions are caught (void *)" $ do + result <- try [C.catchBlock| + throw (void *)0xDEADBEEF; + |] + + result `shouldBeCppOtherException` (Just "void*") + Hspec.it "non-exceptions are caught (std::string)" $ do result <- try [C.catchBlock| throw std::string("FOOBAR");