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.bergmann@allotropia.de>
This commit is contained in:
parent
54f7308641
commit
eedbe966bb
@ -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)
|
||||
|
@ -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)))
|
||||
|
16
bridges/StaticLibrary_emscriptencxxabi.mk
Normal file
16
bridges/StaticLibrary_emscriptencxxabi.mk
Normal file
@ -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:
|
@ -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)
|
||||
|
@ -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
|
||||
|
53
bridges/source/emscriptencxxabi/cxxabi.cxx
Normal file
53
bridges/source/emscriptencxxabi/cxxabi.cxx
Normal file
@ -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: */
|
107
include/bridges/emscriptencxxabi/cxxabi.hxx
Normal file
107
include/bridges/emscriptencxxabi/cxxabi.hxx
Normal file
@ -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: */
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user