bridges/Library_cpp_uno.mk                         |    3 
 bridges/Module_bridges.mk                          |    5 
 bridges/StaticLibrary_emscriptencxxabi.mk          |   16 +++
 bridges/source/cpp_uno/gcc3_wasm/abi.cxx           |   42 --------
 bridges/source/cpp_uno/gcc3_wasm/abi.hxx           |   82 ----------------
 bridges/source/emscriptencxxabi/cxxabi.cxx         |   53 ++++++++++
 include/bridges/emscriptencxxabi/cxxabi.hxx        |  107 +++++++++++++++++++++
 static/StaticLibrary_unoembind.mk                  |    4 
 static/emscripten/uno.js                           |   12 ++
 static/source/unoembindhelpers/PrimaryBindings.cxx |   23 ++++
 unotest/source/embindtest/embindtest.js            |   23 ++--
 11 files changed, 238 insertions(+), 132 deletions(-)

New commits:
commit eedbe966bba1c070041b3a3ff836b4ef28e5bdaa
Author:     Stephan Bergmann <stephan.bergm...@allotropia.de>
AuthorDate: Fri Jun 21 09:58:07 2024 +0200
Commit:     Stephan Bergmann <stephan.bergm...@allotropia.de>
CommitDate: Fri Jun 21 11:29:59 2024 +0200

    Embind: Fix C++ UNO exception catching
    
    ...with a new Module.catchUnoException JS function that can be used in a JS
    catch block to provide the thrown UNO exception as an Any.  (For a non-C++
    exception, it rethrows the exception, and for a non-UNO C++ exception it 
maps it
    to css.uno.RuntimeException.)
    
    The implementation reuses parts of bridges/source/cpp_uno/gcc3_wasm/, which 
have
    been moved to a new StaticLibrary_emscriptencxxabi.
    
    Change-Id: I708fe6121c43a1b9736de5dff449f6c4f32a45f3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169325
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <stephan.bergm...@allotropia.de>

diff --git a/bridges/Library_cpp_uno.mk b/bridges/Library_cpp_uno.mk
index 5d6e7c489802..5ee781e7aa8c 100644
--- a/bridges/Library_cpp_uno.mk
+++ b/bridges/Library_cpp_uno.mk
@@ -90,6 +90,9 @@ $(eval $(call 
gb_Library_add_generated_asmobjects,$(CPPU_ENV)_uno, \
 $(eval $(call gb_Library_add_generated_exception_objects,$(CPPU_ENV)_uno, \
     CustomTarget/bridges/gcc3_wasm/callvirtualfunction-wrapper \
 ))
+$(eval $(call gb_Library_use_static_libraries,$(CPPU_ENV)_uno, \
+    emscriptencxxabi \
+))
 endif
 
 else ifeq ($(CPUNAME),M68K)
diff --git a/bridges/Module_bridges.mk b/bridges/Module_bridges.mk
index def86fea4cb7..a943e6537653 100644
--- a/bridges/Module_bridges.mk
+++ b/bridges/Module_bridges.mk
@@ -20,7 +20,10 @@ $(eval $(call gb_Module_add_targets,bridges,\
                $(if $(filter ANDROID LINUX,$(OS)),\
                        CustomTarget_gcc3_linux_arm) \
        ) \
-       $(if $(filter EMSCRIPTEN,$(OS)),CustomTarget_gcc3_wasm) \
+       $(if $(filter EMSCRIPTEN,$(OS)), \
+           CustomTarget_gcc3_wasm \
+           StaticLibrary_emscriptencxxabi \
+       ) \
 ))
 
 ifeq (,$(filter build,$(gb_Module_SKIPTARGETS)))
diff --git a/bridges/StaticLibrary_emscriptencxxabi.mk 
b/bridges/StaticLibrary_emscriptencxxabi.mk
new file mode 100644
index 000000000000..2271f5734fe5
--- /dev/null
+++ b/bridges/StaticLibrary_emscriptencxxabi.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 
100 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_StaticLibrary_StaticLibrary,emscriptencxxabi))
+
+$(eval $(call gb_StaticLibrary_add_exception_objects,emscriptencxxabi, \
+    bridges/source/emscriptencxxabi/cxxabi \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/bridges/source/cpp_uno/gcc3_wasm/abi.cxx 
b/bridges/source/cpp_uno/gcc3_wasm/abi.cxx
index 5d21cd1d8522..49d7762ea3c2 100644
--- a/bridges/source/cpp_uno/gcc3_wasm/abi.cxx
+++ b/bridges/source/cpp_uno/gcc3_wasm/abi.cxx
@@ -10,12 +10,11 @@
 #include <sal/config.h>
 
 #include <cassert>
-#include <cstddef>
 #include <typeinfo>
 
+#include <bridges/emscriptencxxabi/cxxabi.hxx>
 #include <com/sun/star/uno/RuntimeException.hpp>
 #include <cppu/unotype.hxx>
-#include <rtl/ustrbuf.hxx>
 #include <rtl/ustring.hxx>
 #include <typelib/typedescription.h>
 #include <uno/any2.h>
@@ -23,49 +22,12 @@
 
 #include "abi.hxx"
 
-namespace
-{
-OUString toUnoName(char const* name)
-{
-    assert(name != nullptr);
-    OUStringBuffer b;
-    bool scoped = *name == 'N';
-    if (scoped)
-    {
-        ++name;
-    }
-    for (;;)
-    {
-        assert(*name >= '0' && *name <= '9');
-        std::size_t n = *name++ - '0';
-        while (*name >= '0' && *name <= '9')
-        {
-            n = 10 * n + (*name++ - '0');
-        }
-        b.appendAscii(name, n);
-        name += n;
-        if (!scoped)
-        {
-            assert(*name == 0);
-            break;
-        }
-        if (*name == 'E')
-        {
-            assert(name[1] == 0);
-            break;
-        }
-        b.append('.');
-    }
-    return b.makeStringAndClear();
-}
-}
-
 void abi_wasm::mapException(__cxxabiv1::__cxa_exception* exception, 
std::type_info const* type,
                             uno_Any* any, uno_Mapping* mapping)
 {
     assert(exception != nullptr);
     assert(type != nullptr);
-    OUString unoName(toUnoName(type->name()));
+    OUString unoName(emscriptencxxabi::toUnoName(type->name()));
     typelib_TypeDescription* td = nullptr;
     typelib_typedescription_getByName(&td, unoName.pData);
     if (td == nullptr)
diff --git a/bridges/source/cpp_uno/gcc3_wasm/abi.hxx 
b/bridges/source/cpp_uno/gcc3_wasm/abi.hxx
index 74e92bb9bf0a..e09b68ebd76f 100644
--- a/bridges/source/cpp_uno/gcc3_wasm/abi.hxx
+++ b/bridges/source/cpp_uno/gcc3_wasm/abi.hxx
@@ -11,95 +11,15 @@
 
 #include <sal/config.h>
 
-#include <cstddef>
-#include <cstdint>
-#include <exception>
 #include <typeinfo>
 
 #include <cxxabi.h>
 
+#include <bridges/emscriptencxxabi/cxxabi.hxx>
 #include <config_cxxabi.h>
 #include <uno/any2.h>
 #include <uno/mapping.h>
 
-#if !HAVE_CXXABI_H_CXA_EXCEPTION
-
-// <https://github.com/emscripten-core/emscripten/>, 
system/lib/libunwind/include/unwind_itanium.h,
-// except where MODIFIED:
-typedef std::/*MODIFIED*/ uint64_t _Unwind_Exception_Class;
-struct _Unwind_Exception
-{
-    _Unwind_Exception_Class exception_class;
-    void (*exception_cleanup)(/*MODIFIED: _Unwind_Reason_Code reason, 
_Unwind_Exception* exc*/);
-#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
-    std::/*MODIFIED*/ uintptr_t private_[6];
-#elif !defined(__USING_WASM_EXCEPTIONS__)
-    std::/*MODIFIED*/ uintptr_t private_1; // non-zero means forced unwind
-    std::/*MODIFIED*/ uintptr_t private_2; // holds sp that phase1 found for 
phase2 to use
-#endif
-#if __SIZEOF_POINTER__ == 4
-    // The implementation of _Unwind_Exception uses an attribute mode on the
-    // above fields which has the side effect of causing this whole struct to
-    // round up to 32 bytes in size (48 with SEH). To be more explicit, we add
-    // pad fields added for binary compatibility.
-    std::/*MODIFIED*/ uint32_t reserved[3];
-#endif
-    // The Itanium ABI requires that _Unwind_Exception objects are "double-word
-    // aligned".  GCC has interpreted this to mean "use the maximum useful
-    // alignment for the target"; so do we.
-} __attribute__((__aligned__));
-
-// <https://github.com/emscripten-core/emscripten/>, 
system/lib/libcxxabi/src/cxa_exception.h,
-// except where MODIFIED:
-namespace __cxxabiv1
-{
-struct __cxa_exception
-{
-#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)
-    // Now _Unwind_Exception is marked with __attribute__((aligned)),
-    // which implies __cxa_exception is also aligned. Insert padding
-    // in the beginning of the struct, rather than before unwindHeader.
-    void* reserve;
-    // This is a new field to support C++11 exception_ptr.
-    // For binary compatibility it is at the start of this
-    // struct which is prepended to the object thrown in
-    // __cxa_allocate_exception.
-    std::/*MODIFIED*/ size_t referenceCount;
-#endif
-    //  Manage the exception object itself.
-    std::type_info* exceptionType;
-#if 1 //MODIFIED: #ifdef __USING_WASM_EXCEPTIONS__
-    // In wasm, destructors return their argument
-    void*(/*MODIFIED: _LIBCXXABI_DTOR_FUNC*/ *exceptionDestructor)(void*);
-#else
-    void(/*MODIFIED: _LIBCXXABI_DTOR_FUNC*/ *exceptionDestructor)(void*);
-#endif
-    void* /*MODIFIED: std::unexpected_handler*/ unexpectedHandler;
-    std::terminate_handler terminateHandler;
-    __cxa_exception* nextException;
-    int handlerCount;
-#if defined(_LIBCXXABI_ARM_EHABI)
-    __cxa_exception* nextPropagatingException;
-    int propagationCount;
-#else
-    int handlerSwitchValue;
-    const unsigned char* actionRecord;
-    const unsigned char* languageSpecificData;
-    void* catchTemp;
-    void* adjustedPtr;
-#endif
-#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)
-    // This is a new field to support C++11 exception_ptr.
-    // For binary compatibility it is placed where the compiler
-    // previously added padding to 64-bit align unwindHeader.
-    std::/*MODIFIED*/ size_t referenceCount;
-#endif
-    _Unwind_Exception unwindHeader;
-};
-}
-
-#endif
-
 #if !HAVE_CXXABI_H_CXA_EH_GLOBALS
 // <https://github.com/emscripten-core/emscripten/>, 
system/lib/libcxxabi/src/cxa_exception.h:
 namespace __cxxabiv1
diff --git a/bridges/source/emscriptencxxabi/cxxabi.cxx 
b/bridges/source/emscriptencxxabi/cxxabi.cxx
new file mode 100644
index 000000000000..19aaae7847b6
--- /dev/null
+++ b/bridges/source/emscriptencxxabi/cxxabi.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+
+#include <bridges/emscriptencxxabi/cxxabi.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+
+OUString emscriptencxxabi::toUnoName(char const* name)
+{
+    assert(name != nullptr);
+    OUStringBuffer b;
+    bool scoped = *name == 'N';
+    if (scoped)
+    {
+        ++name;
+    }
+    for (;;)
+    {
+        assert(*name >= '0' && *name <= '9');
+        std::size_t n = *name++ - '0';
+        while (*name >= '0' && *name <= '9')
+        {
+            n = 10 * n + (*name++ - '0');
+        }
+        b.appendAscii(name, n);
+        name += n;
+        if (!scoped)
+        {
+            assert(*name == 0);
+            break;
+        }
+        if (*name == 'E')
+        {
+            assert(name[1] == 0);
+            break;
+        }
+        b.append('.');
+    }
+    return b.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/bridges/emscriptencxxabi/cxxabi.hxx 
b/include/bridges/emscriptencxxabi/cxxabi.hxx
new file mode 100644
index 000000000000..8c380961cb60
--- /dev/null
+++ b/include/bridges/emscriptencxxabi/cxxabi.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <exception>
+#include <typeinfo>
+
+#include <cxxabi.h>
+
+#include <config_cxxabi.h>
+#include <rtl/ustring.hxx>
+
+#if !HAVE_CXXABI_H_CXA_EXCEPTION
+
+// <https://github.com/emscripten-core/emscripten/>, 
system/lib/libunwind/include/unwind_itanium.h,
+// except where MODIFIED:
+typedef std::/*MODIFIED*/ uint64_t _Unwind_Exception_Class;
+struct _Unwind_Exception
+{
+    _Unwind_Exception_Class exception_class;
+    void (*exception_cleanup)(/*MODIFIED: _Unwind_Reason_Code reason, 
_Unwind_Exception* exc*/);
+#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
+    std::/*MODIFIED*/ uintptr_t private_[6];
+#elif !defined(__USING_WASM_EXCEPTIONS__)
+    std::/*MODIFIED*/ uintptr_t private_1; // non-zero means forced unwind
+    std::/*MODIFIED*/ uintptr_t private_2; // holds sp that phase1 found for 
phase2 to use
+#endif
+#if __SIZEOF_POINTER__ == 4
+    // The implementation of _Unwind_Exception uses an attribute mode on the
+    // above fields which has the side effect of causing this whole struct to
+    // round up to 32 bytes in size (48 with SEH). To be more explicit, we add
+    // pad fields added for binary compatibility.
+    std::/*MODIFIED*/ uint32_t reserved[3];
+#endif
+    // The Itanium ABI requires that _Unwind_Exception objects are "double-word
+    // aligned".  GCC has interpreted this to mean "use the maximum useful
+    // alignment for the target"; so do we.
+} __attribute__((__aligned__));
+
+// <https://github.com/emscripten-core/emscripten/>, 
system/lib/libcxxabi/src/cxa_exception.h,
+// except where MODIFIED:
+namespace __cxxabiv1
+{
+struct __cxa_exception
+{
+#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)
+    // Now _Unwind_Exception is marked with __attribute__((aligned)),
+    // which implies __cxa_exception is also aligned. Insert padding
+    // in the beginning of the struct, rather than before unwindHeader.
+    void* reserve;
+    // This is a new field to support C++11 exception_ptr.
+    // For binary compatibility it is at the start of this
+    // struct which is prepended to the object thrown in
+    // __cxa_allocate_exception.
+    std::/*MODIFIED*/ size_t referenceCount;
+#endif
+    //  Manage the exception object itself.
+    std::type_info* exceptionType;
+#if 1 //MODIFIED: #ifdef __USING_WASM_EXCEPTIONS__
+    // In wasm, destructors return their argument
+    void*(/*MODIFIED: _LIBCXXABI_DTOR_FUNC*/ *exceptionDestructor)(void*);
+#else
+    void(/*MODIFIED: _LIBCXXABI_DTOR_FUNC*/ *exceptionDestructor)(void*);
+#endif
+    void* /*MODIFIED: std::unexpected_handler*/ unexpectedHandler;
+    std::terminate_handler terminateHandler;
+    __cxa_exception* nextException;
+    int handlerCount;
+#if defined(_LIBCXXABI_ARM_EHABI)
+    __cxa_exception* nextPropagatingException;
+    int propagationCount;
+#else
+    int handlerSwitchValue;
+    const unsigned char* actionRecord;
+    const unsigned char* languageSpecificData;
+    void* catchTemp;
+    void* adjustedPtr;
+#endif
+#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)
+    // This is a new field to support C++11 exception_ptr.
+    // For binary compatibility it is placed where the compiler
+    // previously added padding to 64-bit align unwindHeader.
+    std::/*MODIFIED*/ size_t referenceCount;
+#endif
+    _Unwind_Exception unwindHeader;
+};
+}
+
+#endif
+
+namespace emscriptencxxabi
+{
+OUString toUnoName(char const* name);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/static/StaticLibrary_unoembind.mk 
b/static/StaticLibrary_unoembind.mk
index e2a2bfacd892..754c591c4efb 100644
--- a/static/StaticLibrary_unoembind.mk
+++ b/static/StaticLibrary_unoembind.mk
@@ -22,6 +22,10 @@ $(eval $(call gb_StaticLibrary_use_api,unoembind,\
     udkapi \
 ))
 
+$(eval $(call gb_StaticLibrary_use_static_libraries,unoembind, \
+    emscriptencxxabi \
+))
+
 $(call gb_StaticLibrary_get_target,unoembind): $(call 
gb_CustomTarget_get_target,static/unoembind)
 
 # vim: set noet sw=4 ts=4:
diff --git a/static/emscripten/uno.js b/static/emscripten/uno.js
index cb7276d19489..bd1e693a6e77 100644
--- a/static/emscripten/uno.js
+++ b/static/emscripten/uno.js
@@ -15,6 +15,18 @@ Module.initUno = function() {
     }
 };
 
+Module.catchUnoException = function(exception) {
+    // Rethrow non-C++ exceptions (non-UNO C++ exceptions are mapped to 
css.uno.RuntimeException in
+    // Module.getUnoExceptionFromCxaException):
+    if (!(exception instanceof WebAssembly.Exception && 
exception.is(getCppExceptionTag()))) {
+        throw exception;
+    }
+    const any = Module.getUnoExceptionFromCxaException(
+        getCppExceptionThrownObjectFromWebAssemblyException(exception));
+    decrementExceptionRefcount(exception);
+    return any;
+}
+
 Module.unoObject = function(interfaces, obj) {
     Module.initUno();
     interfaces = ['com.sun.star.lang.XTypeProvider'].concat(interfaces);
diff --git a/static/source/unoembindhelpers/PrimaryBindings.cxx 
b/static/source/unoembindhelpers/PrimaryBindings.cxx
index a8c7acab9250..f1e57f281220 100644
--- a/static/source/unoembindhelpers/PrimaryBindings.cxx
+++ b/static/source/unoembindhelpers/PrimaryBindings.cxx
@@ -12,6 +12,7 @@
 #include <emscripten.h>
 #include <emscripten/bind.h>
 
+#include <bridges/emscriptencxxabi/cxxabi.hxx>
 #include <com/sun/star/uno/Any.hxx>
 #include <com/sun/star/uno/Reference.hxx>
 #include <com/sun/star/uno/RuntimeException.hpp>
@@ -414,6 +415,28 @@ EMSCRIPTEN_BINDINGS(PrimaryBindings)
                  css::uno::Reference<css::uno::XInterface> const& ref2) { 
return ref1 == ref2; });
     function("rtl_uString_release",
              +[](std::uintptr_t ptr) { 
rtl_uString_release(reinterpret_cast<rtl_uString*>(ptr)); });
+    function("getUnoExceptionFromCxaException", +[](std::uintptr_t ptr) {
+        // Cf. __get_exception_message in 
<https://github.com/emscripten-core/emscripten/>,
+        // system/lib/libcxxabi/src/cxa_exception_js_utils.cpp:
+        auto const header = reinterpret_cast<__cxxabiv1::__cxa_exception 
const*>(ptr) - 1;
+        css::uno::Any exc;
+        OUString 
unoName(emscriptencxxabi::toUnoName(header->exceptionType->name()));
+        typelib_TypeDescription* td = nullptr;
+        typelib_typedescription_getByName(&td, unoName.pData);
+        if (td == nullptr)
+        {
+            css::uno::RuntimeException e("exception type not found: " + 
unoName);
+            uno_type_any_construct(
+                &exc, &e, 
cppu::UnoType<css::uno::RuntimeException>::get().getTypeLibType(),
+                cpp_acquire);
+        }
+        else
+        {
+            uno_any_construct(&exc, reinterpret_cast<void*>(ptr), td, 
cpp_acquire);
+            typelib_typedescription_release(td);
+        }
+        return exc;
+    });
 
     jsRegisterChar(&typeid(char16_t));
     jsRegisterString(&typeid(OUString));
diff --git a/unotest/source/embindtest/embindtest.js 
b/unotest/source/embindtest/embindtest.js
index 9f4276197c3d..d47fed4703ab 100644
--- a/unotest/source/embindtest/embindtest.js
+++ b/unotest/source/embindtest/embindtest.js
@@ -640,11 +640,11 @@ Module.addOnPostRun(function() {
         test.throwRuntimeException();
         console.assert(false);
     } catch (e) {
-        const [type, message] = getExceptionMessage(e);
-        console.assert(type === 'com::sun::star::uno::RuntimeException');
-        console.assert(message === undefined); //TODO
-        //TODO: verify css.uno.RuntimeException's Message startsWith('test')
-        decrementExceptionRefcount(e);
+        const any = Module.catchUnoException(e);
+        console.assert(any.getType() == 'com.sun.star.uno.RuntimeException');
+        const exc = any.get();
+        console.assert(exc.Message.startsWith('test'));
+        any.delete();
     }
     const obj = Module.unoObject(
         ['com.sun.star.task.XJob', 'com.sun.star.task.XJobExecutor'],
@@ -1086,11 +1086,14 @@ Module.addOnPostRun(function() {
             console.assert(false);
             ret.delete();
         } catch (e) {
-            const [type, message] = getExceptionMessage(e);
-            console.assert(type === 
'com::sun::star::reflection::InvocationTargetException');
-            console.assert(message === undefined); //TODO
-            //TODO: inspect wrapped css.uno.RuntimeException
-            decrementExceptionRefcount(e);
+            const any = Module.catchUnoException(e);
+            console.assert(any.getType() == 
'com.sun.star.reflection.InvocationTargetException');
+            const target = any.get().TargetException;
+            console.assert(target.getType() == 
'com.sun.star.uno.RuntimeException');
+            const exc = target.get();
+            console.assert(exc.Message.startsWith('test'));
+            any.delete();
+            target.delete();
         }
         params.delete();
         outparamindex.delete();

Reply via email to