Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions inline-c-cpp/cxx-src/HaskellException.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,51 @@ const char* HaskellException::what() const noexcept {
// <https://stackoverflow.com/questions/561997/determining-exception-type-after-the-exception-is-caught/47164539#47164539>
// 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,
- "<unknown exception>" if exception type info is not available,
- "<no exception>" 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("<no exception>");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated to the PR (it was like this to begin with), but could you update the comment in Exception.hs referencing free, while it's actually using unsafePackMallocCString, and also note here that these allocations are freed there?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a bunch of comments of this sort. I think they do, but do they cover your request?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do, thank you


const char *raw_name = type_info->name();
if (!raw_name)
return strdup("<unknown exception>");

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<char*>(std::malloc(message_len));
std::memcpy(*typStrPtr, message, message_len);
*typStrPtr = currentExceptionTypeName();
#else
*typStrPtr = NULL;
#endif
Expand Down
4 changes: 2 additions & 2 deletions inline-c-cpp/include/HaskellException.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
25 changes: 22 additions & 3 deletions inline-c-cpp/src/Language/C/Inline/Cpp/Exception.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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 #-}
Expand Down Expand Up @@ -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
Expand All @@ -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."

Expand Down Expand Up @@ -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 {"
Expand Down
7 changes: 7 additions & 0 deletions inline-c-cpp/test/tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down